diff options
| author | Fuwn <[email protected]> | 2026-03-01 14:20:08 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-03-01 15:24:03 -0800 |
| commit | 3b10a1f47fd5838fe3b94c19673a52610b88cf1e (patch) | |
| tree | d468a1fc12290e38686b255194ff6596b58cbf01 /src/lib/Utility/html.ts | |
| parent | perf(match): fast-path exact normalised title matches (diff) | |
| download | due.moe-3b10a1f47fd5838fe3b94c19673a52610b88cf1e.tar.xz due.moe-3b10a1f47fd5838fe3b94c19673a52610b88cf1e.zip | |
perf: optimise list hot paths and shared timers
Diffstat (limited to 'src/lib/Utility/html.ts')
| -rw-r--r-- | src/lib/Utility/html.ts | 109 |
1 files changed, 79 insertions, 30 deletions
diff --git a/src/lib/Utility/html.ts b/src/lib/Utility/html.ts index 8d60d3b6..ccdfae8d 100644 --- a/src/lib/Utility/html.ts +++ b/src/lib/Utility/html.ts @@ -4,39 +4,88 @@ import { get } from 'svelte/store'; export const nbsp = (str: string) => str.replace(/ /g, ' '); export const createHeightObserver = (details = true) => { - document.querySelectorAll('.list').forEach((element) => { - if ( - !( - element as unknown as { - dataset: { observed: string }; - } - ).dataset.observed - ) { - new ResizeObserver((entries) => { - entries.forEach((entry) => { - const element = entry.target as HTMLElement; - - if (get(settings).displayLimitListHeight) { - element.style.height = 'auto'; - - const elementBound = element.getBoundingClientRect(); - const height = window.innerHeight - elementBound.top - 2.5 * 16; - - if (elementBound.height > height) element.style.height = `${height}px`; - } - }); - }).observe(element); + const observedElements = new Set<HTMLElement>(); + const resizeObservers = new Map<HTMLElement, ResizeObserver>(); + const detailObservers = new Map<HTMLElement, MutationObserver>(); + + const applyHeightLimit = (target: HTMLElement) => { + if (!get(settings).displayLimitListHeight) { + target.style.height = 'auto'; + + return; + } + + target.style.height = 'auto'; + + const elementBound = target.getBoundingClientRect(); + const height = window.innerHeight - elementBound.top - 2.5 * 16; - if (details) - new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - const element = mutation.target as HTMLDetailsElement; + if (elementBound.height > height) target.style.height = `${height}px`; + }; - if (element.tagName === 'DETAILS' && !element.open) element.style.height = 'auto'; - }); - }).observe(element, { attributes: true }); + const observeElement = (element: HTMLElement) => { + if (element.dataset.observed) return; + + const resizeObserver = new ResizeObserver((entries) => { + entries.forEach((entry) => { + const target = entry.target as HTMLElement; + + applyHeightLimit(target); + }); + }); + + resizeObserver.observe(element); + resizeObservers.set(element, resizeObserver); + + if (details) { + const detailsObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + const target = mutation.target as HTMLDetailsElement; + + if (target.tagName === 'DETAILS' && !target.open) target.style.height = 'auto'; + }); + }); - element.setAttribute('data-observed', 'true'); + detailsObserver.observe(element, { attributes: true }); + detailObservers.set(element, detailsObserver); } + + element.dataset.observed = 'true'; + + observedElements.add(element); + applyHeightLimit(element); + }; + + document.querySelectorAll<HTMLElement>('.list').forEach(observeElement); + + const mutationObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + mutation.addedNodes.forEach((node) => { + if (!(node instanceof HTMLElement)) return; + + if (node.matches('.list')) observeElement(node); + + node.querySelectorAll<HTMLElement>('.list').forEach(observeElement); + }); + }); }); + + mutationObserver.observe(document.body, { childList: true, subtree: true }); + + const unsubscribeSettings = settings.subscribe(() => { + observedElements.forEach((element) => applyHeightLimit(element)); + }); + + return () => { + unsubscribeSettings(); + mutationObserver.disconnect(); + resizeObservers.forEach((observer) => observer.disconnect()); + detailObservers.forEach((observer) => observer.disconnect()); + + observedElements.forEach((element) => { + element.style.height = 'auto'; + + delete element.dataset.observed; + }); + }; }; |