outliner: refactor to increase performance

reduced the number of times the outliner has to refresh
in response to issue #333
This commit is contained in:
CloudHill 2020-12-09 01:34:59 +07:00
parent 07d0836e97
commit 2d43b79735
2 changed files with 48 additions and 14 deletions

View File

@ -14,7 +14,7 @@ module.exports = {
tags: ['extension', 'panel'], tags: ['extension', 'panel'],
name: 'outliner', name: 'outliner',
desc: 'table of contents.', desc: 'table of contents.',
version: '1.1.1', version: '1.2.0',
author: 'CloudHill', author: 'CloudHill',
options: [ options: [
{ {

View File

@ -10,6 +10,8 @@
const { createElement } = require("../../pkg/helpers"); const { createElement } = require("../../pkg/helpers");
module.exports = (store, __exports) => { module.exports = (store, __exports) => {
let lastSearch;
// Observe for page changes // Observe for page changes
const pageObserver = new MutationObserver((list, observer) => { const pageObserver = new MutationObserver((list, observer) => {
for ( let { addedNodes } of list) { for ( let { addedNodes } of list) {
@ -30,18 +32,30 @@ module.exports = (store, __exports) => {
// Observe for header changes // Observe for header changes
const contentObserver = new MutationObserver((list, observer) => { const contentObserver = new MutationObserver((list, observer) => {
list.forEach(m => { list.forEach(m => {
let header;
if ( if (
( (
m.type === 'childList' && m.type === 'childList' &&
( (
isHeaderElement(m.target) || m.target.hasAttribute('placeholder') ||
isHeaderElement(m.addedNodes[0]) || m.target.className?.includes('header-block')
isHeaderElement(m.removedNodes[0]) ) &&
(
(header = getHeaderBlock(m.target)) ||
(header = getHeaderBlock(m.addedNodes[0]))
) )
) || ) ||
( (
m.type === 'characterData' && m.type === 'characterData' &&
isHeaderElement(m.target.parentElement) (header = getHeaderBlock(m.target.parentElement))
)
) updateOutlineHeader(header);
else if (
m.type === 'childList' && m.removedNodes[0] &&
(
isHeaderElement(m.removedNodes[0]) ||
m.removedNodes[0].querySelector?.('[class*="header-block"]')
) )
) findHeaders(); ) findHeaders();
}) })
@ -61,6 +75,10 @@ module.exports = (store, __exports) => {
} }
function findHeaders() { function findHeaders() {
// Add cooldown to prevent the function being run twice at the 'same' time
if (lastSearch >= (Date.now() - 10)) return;
lastSearch = Date.now();
const outline = document.querySelector('.outliner'); const outline = document.querySelector('.outliner');
if (!outline) return; if (!outline) return;
outline.textContent = ''; outline.textContent = '';
@ -80,22 +98,38 @@ module.exports = (store, __exports) => {
outline-placeholder="${placeholder}"></a> outline-placeholder="${placeholder}"></a>
</div> </div>
`); `);
header.outline = outlineHeader;
outlineHeader.firstElementChild.innerHTML = headerEl.innerHTML; outlineHeader.firstElementChild.innerHTML = headerEl.innerHTML;
outline.append(outlineHeader); outline.append(outlineHeader);
}) })
} }
function updateOutlineHeader(header) {
const headerEl = header.querySelector('[placeholder]') || header;
if (!(
headerEl &&
header.outline &&
header.outline.parentElement
)) return findHeaders();
const outlineHeader = header.outline;
outlineHeader.firstElementChild.innerHTML = headerEl.innerHTML;
updateOutlineLevel(outlineHeader, headerEl.getAttribute('placeholder').slice(-1));
}
function updateOutlineLevel(outlineHeader, level) {
outlineHeader.setAttribute('header-level', level);
outlineHeader.firstElementChild.setAttribute('outline-placeholder', `Header ${level}`)
}
function getHeaderBlock(el) {
return el?.closest?.('[class*="header-block"]');
}
function isHeaderElement(el) { function isHeaderElement(el) {
let placeholder; let placeholder;
if (el) { if (el) {
if ( placeholder = el.getAttribute?.('placeholder') ||
el.querySelector && el.querySelector?.('[placeholder]')?.getAttribute('placeholder');
el.querySelector('[placeholder]')
) {
placeholder = el.querySelector('[placeholder]').getAttribute('placeholder')
} else if (el.getAttribute) {
placeholder = el.getAttribute('placeholder');
}
} }
if (!placeholder) placeholder = ''; if (!placeholder) placeholder = '';
return placeholder.includes('Heading'); return placeholder.includes('Heading');