From 9cebf91e2809204e1d1da7a0eb18f07568622b8c Mon Sep 17 00:00:00 2001 From: tophf Date: Thu, 2 Aug 2018 00:46:14 +0300 Subject: [PATCH] don't call updatePreview twice for the same input event --- edit/codemirror-editing-hooks.js | 45 +++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/edit/codemirror-editing-hooks.js b/edit/codemirror-editing-hooks.js index 253c55d4..8ac7d69a 100644 --- a/edit/codemirror-editing-hooks.js +++ b/edit/codemirror-editing-hooks.js @@ -618,14 +618,53 @@ onDOMscriptReady('/codemirror.js').then(() => { const me = this instanceof Node ? this : $('#editor.livePreview'); const previewing = me.checked; editors.forEach(cm => cm[previewing ? 'on' : 'off']('changes', updatePreview)); - const addRemove = previewing ? 'addEventListener' : 'removeEventListener'; - $('#enabled')[addRemove]('change', updatePreview); - $('#sections')[addRemove]('input', updatePreview); + const addRemove = EventTarget.prototype[previewing ? 'addEventListener' : 'removeEventListener']; + addRemove.call($('#enabled'), 'change', updatePreview); + if (!editor) { + for (const el of $$('#sections .applies-to')) { + addRemove.call(el, 'input', updatePreview); + } + toggleLivePreviewSectionsObserver(previewing); + } if (!previewing || document.body.classList.contains('dirty')) { updatePreview(null, previewing); } } + /** + * Observes newly added section elements and sets an 'input' event listener on .applies-to inside. + * The goal is to avoid listening to 'input' on the entire #sections tree, + * which would trigger updatePreview() twice on any keystroke - + * both for the synthetic event from CodeMirror and the original event. + * Side effects: + * two expando properties on #sections + * 1. __livePreviewObserver + * 2. __livePreviewObserverEnabled + * @param {Boolean} enable + */ + function toggleLivePreviewSectionsObserver(enable) { + const sections = $('#sections'); + const observing = sections.__livePreviewObserverEnabled; + let mo = sections.__livePreviewObserver; + if (enable && !mo) { + sections.__livePreviewObserver = mo = new MutationObserver(mutations => { + for (const {addedNodes} of mutations) { + for (const node of addedNodes) { + const el = node.children && $('.applies-to', node); + if (el) el.addEventListener('input', updatePreview); + } + } + }); + } + if (enable && !observing) { + mo.observe(sections, {childList: true}); + sections.__livePreviewObserverEnabled = true; + } else if (!enable && observing) { + mo.disconnect(); + sections.__livePreviewObserverEnabled = false; + } + } + function updatePreview(data, previewing) { if (previewing !== true && previewing !== false) { if (data instanceof Event && !data.target.matches('.style-contributor')) return;