From dfc6f6c5e24b5587d52f2955940f4db1fcf586db Mon Sep 17 00:00:00 2001 From: Andreas Fahrecker Date: Wed, 2 Sep 2026 19:43:33 +0200 Subject: [PATCH] perf: :zap: batch elements into shared IntersectionObserver instances per threshold --- src/observer/index.js | 71 ++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/src/observer/index.js b/src/observer/index.js index cf27bb2..e710d6e 100644 --- a/src/observer/index.js +++ b/src/observer/index.js @@ -1,3 +1,13 @@ +const OBSERVE_SELECTORS = [ + '[class*=" intersect:"]', + '[class*=":intersect:"]', + '[class^="intersect:"]', + '[class="intersect"]', + '[class*=" intersect "]', + '[class^="intersect "]', + '[class$=" intersect"]', +] + const Observer = { start() { if (document.readyState === 'loading') { @@ -17,35 +27,40 @@ const Observer = { }, observe() { - const selectors = [ - '[class*=" intersect:"]', - '[class*=":intersect:"]', - '[class^="intersect:"]', - '[class="intersect"]', - '[class*=" intersect "]', - '[class^="intersect "]', - '[class$=" intersect"]' - ] - - document.querySelectorAll(selectors.join(',')).forEach(element => { - const observer = new IntersectionObserver(entries => { - entries.forEach(entry => { - if (! entry.isIntersecting) { - element.setAttribute('no-intersect', '') - - return - } - - element.removeAttribute('no-intersect') - - element.classList.contains('intersect-once') && observer.disconnect() - }) - }, { - threshold: this._getThreshold(element), - }) - - observer.observe(element) + const elements = document.querySelector(OBSERVE_SELECTORS.join(',')) + /** + * @type {Map>} + */ + const byThreshold = new Map() + + elements.forEach((element) => { + const threshold = this._getThreshold(element) + if (!byThreshold.has(threshold)) + byThreshold.set(threshold, new Set()) + byThreshold.get(threshold).add(element) + }) + byThreshold.forEach((elementSet, threshold) => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + const element = entry.target + if (!entry.isIntersecting) { + element.setAttribute('no-intersect', '') + return + } + + element.removeAttribute('no-intersect') + if (element.classList.contains('intersect-once')) + observer.unobserve(element) + }) + }, + { + threshold, + }, + ) + + elementSet.forEach((element) => observer.observe(element)) this._observers.push(observer) }) },