From a73e07eac06a70f63896e2bb7f0c205534dc21c8 Mon Sep 17 00:00:00 2001 From: Luffy Date: Mon, 6 Jan 2025 11:43:40 +0800 Subject: [PATCH] fix: Prevent initial unnecessary IntersectionObserver callback execution (#2523) --- src/core/event/index.js | 39 ++++++++++++++++++++++++------------- src/core/render/compiler.js | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/core/event/index.js b/src/core/event/index.js index ef8a39b4c..fe4076f05 100644 --- a/src/core/event/index.js +++ b/src/core/event/index.js @@ -45,7 +45,7 @@ export function Events(Base) { // ========================================================================= /** * Initialize cover observer - * Toggles sticky behavior when when cover is not in view + * Toggles sticky behavior when cover is not in view * @void */ #initCover() { @@ -74,11 +74,17 @@ export function Events(Base) { #initHeadings() { const headingElms = dom.findAll('#main :where(h1, h2, h3, h4, h5)'); const headingsInView = new Set(); + let isInitialLoad = true; // Mark sidebar active item on heading intersection this.#intersectionObserver?.disconnect(); this.#intersectionObserver = new IntersectionObserver( entries => { + if (isInitialLoad) { + isInitialLoad = false; + return; + } + if (this.#isScrolling) { return; } @@ -89,18 +95,25 @@ export function Events(Base) { headingsInView[op](entry.target); } - const activeHeading = - headingsInView.size > 1 - ? // Sort headings by proximity to viewport top and select first - Array.from(headingsInView).sort((a, b) => - a.compareDocumentPosition(b) & - Node.DOCUMENT_POSITION_FOLLOWING - ? -1 - : 1, - )[0] - : // Get first and only item in set. - // May be undefined if no headings are in view. - headingsInView.values().next().value; + let activeHeading; + if (headingsInView.size === 1) { + // Get first and only item in set. + // May be undefined if no headings are in view. + activeHeading = headingsInView.values().next().value; + } else if (headingsInView.size > 1) { + // Find the closest heading to the top of the viewport + // Reduce over the Set of headings currently in view (headingsInView) to determine the closest heading. + activeHeading = Array.from(headingsInView).reduce( + (closest, current) => { + return !closest || + closest.compareDocumentPosition(current) & + Node.DOCUMENT_POSITION_FOLLOWING + ? current + : closest; + }, + null, + ); + } if (activeHeading) { const id = activeHeading.getAttribute('id'); diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js index b86166301..124be9b7f 100644 --- a/src/core/render/compiler.js +++ b/src/core/render/compiler.js @@ -181,7 +181,7 @@ export class Compiler { } /** - * Compile sidebar, it uses _sidebar.md ( or specific file) or the content's headings toc to render sidebar. + * Compile sidebar, it uses _sidebar.md (or specific file) or the content's headings toc to render sidebar. * @param {String} text Text content from the sidebar file, maybe empty * @param {Number} level Type of heading (h tag) * @returns {String} Sidebar element