diff --git a/src/core/event/index.js b/src/core/event/index.js index 516f1fb27..c6cf10a6a 100644 --- a/src/core/event/index.js +++ b/src/core/event/index.js @@ -1,9 +1,8 @@ -import Tweezer from 'tweezer.js'; -import { isMobile } from '../util/env.js'; -import { body, on } from '../util/dom.js'; -import * as dom from '../util/dom.js'; -import { removeParams } from '../router/util.js'; import config from '../config.js'; +import { removeParams } from '../router/util.js'; +import * as dom from '../util/dom.js'; +import { body, on } from '../util/dom.js'; +import { isMobile } from '../util/env.js'; /** @typedef {import('../Docsify.js').Constructor} Constructor */ @@ -18,14 +17,14 @@ export function Events(Base) { // If 'history', rely on the browser's scroll auto-restoration when going back or forward if (source !== 'history') { - // Scroll to ID if specified - if (this.route.query.id) { - this.#scrollIntoView(this.route.path, this.route.query.id); - } // Scroll to top if a link was clicked and auto2top is enabled if (source === 'navigate') { auto2top && this.#scroll2Top(auto2top); } + // Scroll to ID if specified + if (this.route.query.id) { + this.#scrollIntoView(this.route.path, this.route.query.id); + } } if (this.config.loadNavbar) { @@ -49,40 +48,57 @@ export function Events(Base) { #nav = {}; #hoverOver = false; - #scroller = null; #enableScrollEvent = true; #coverHeight = 0; + #delayScrollInterval; + async #scrollTo(el) { + this.#enableScrollEvent = false; - #scrollTo(el, offset = 0) { - if (this.#scroller) { - this.#scroller.stop(); + if (document.readyState !== 'complete') { + clearInterval(this.#delayScrollInterval); + await new Promise(resolve => { + this.#delayScrollInterval = setInterval(() => { + if (document.readyState === 'complete') { + clearInterval(this.#delayScrollInterval); + resolve(); + } + }, 100); + }); } - this.#enableScrollEvent = false; - this.#scroller = new Tweezer({ - start: window.pageYOffset, - end: - Math.round(el.getBoundingClientRect().top) + - window.pageYOffset - - offset, - duration: 500, - }) - .on('tick', v => window.scrollTo(0, v)) - .on('done', () => { + el.scrollIntoView({ behavior: 'smooth' }); + + // Determine when scrolling has stopped + let prevTop; + const completeIntervalId = setInterval(() => { + const top = el.getBoundingClientRect().top; + if (top === prevTop) { + console.log('clearing'); + clearInterval(completeIntervalId); this.#enableScrollEvent = true; - this.#scroller = null; - }) - .begin(); + } + prevTop = top; + }, 500); } - #highlight(path) { + #delayHighlightInterval; + async #highlight(path) { + let delayed = false; if (!this.#enableScrollEvent) { - return; + delayed = true; + clearInterval(this.#delayHighlightInterval); + await new Promise(resolve => { + this.#delayHighlightInterval = setInterval(() => { + if (this.#enableScrollEvent) { + clearInterval(this.#delayHighlightInterval); + resolve(); + } + }, 100); + }); } const sidebar = dom.getNode('.sidebar'); const anchors = dom.findAll('.anchor'); - const wrap = dom.find(sidebar, '.sidebar-nav'); let active = dom.find(sidebar, 'li.active'); const doc = document.documentElement; const top = @@ -107,7 +123,7 @@ export function Events(Base) { const li = this.#nav[this.#getNavKey(path, last.getAttribute('data-id'))]; - if (!li || li === active) { + if (!li || (li === active && !delayed)) { return; } @@ -116,20 +132,8 @@ export function Events(Base) { active = li; // Scroll into view - // https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297 if (!this.#hoverOver && dom.body.classList.contains('sticky')) { - const height = sidebar.clientHeight; - const curOffset = 0; - const cur = active.offsetTop + active.clientHeight + 40; - const isInView = - active.offsetTop >= wrap.scrollTop && cur <= wrap.scrollTop + height; - const notThan = cur - curOffset < height; - - sidebar.scrollTop = isInView - ? wrap.scrollTop - : notThan - ? curOffset - : cur - height; + active.scrollIntoView(); } }