mirror of
https://github.com/docsifyjs/docsify.git
synced 2025-12-08 19:55:52 +00:00
* Adds option to specify an header height When using a template that has a sticky-header, clicking on the sidebar will scroll the page under the header. I added the option `headerHeight` (default = `0`) so that the content div will be scrolled down that amount of pixels. * updates documentation and renames variable
162 lines
3.7 KiB
JavaScript
162 lines
3.7 KiB
JavaScript
import Tweezer from 'tweezer.js';
|
|
import cssEscape from 'css.escape';
|
|
import { isMobile } from '../util/env';
|
|
import * as dom from '../util/dom';
|
|
import config from '../config';
|
|
|
|
const nav = {};
|
|
let hoverOver = false;
|
|
let scroller = null;
|
|
let enableScrollEvent = true;
|
|
let coverHeight = 0;
|
|
|
|
function scrollTo(el, offset = 0) {
|
|
if (scroller) {
|
|
scroller.stop();
|
|
}
|
|
|
|
enableScrollEvent = false;
|
|
scroller = new Tweezer({
|
|
start: window.pageYOffset,
|
|
end: el.getBoundingClientRect().top + window.pageYOffset - offset,
|
|
duration: 500,
|
|
})
|
|
.on('tick', v => window.scrollTo(0, v))
|
|
.on('done', () => {
|
|
enableScrollEvent = true;
|
|
scroller = null;
|
|
})
|
|
.begin();
|
|
}
|
|
|
|
function highlight(path) {
|
|
if (!enableScrollEvent) {
|
|
return;
|
|
}
|
|
|
|
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 = ((doc && doc.scrollTop) || document.body.scrollTop) - coverHeight;
|
|
let last;
|
|
|
|
for (let i = 0, len = anchors.length; i < len; i += 1) {
|
|
const node = anchors[i];
|
|
|
|
if (node.offsetTop > top) {
|
|
if (!last) {
|
|
last = node;
|
|
}
|
|
|
|
break;
|
|
} else {
|
|
last = node;
|
|
}
|
|
}
|
|
|
|
if (!last) {
|
|
return;
|
|
}
|
|
|
|
const li =
|
|
nav[getNavKey(decodeURIComponent(path), last.getAttribute('data-id'))];
|
|
|
|
if (!li || li === active) {
|
|
return;
|
|
}
|
|
|
|
active && active.classList.remove('active');
|
|
li.classList.add('active');
|
|
active = li;
|
|
|
|
// Scroll into view
|
|
// https://github.com/vuejs/vuejs.org/blob/master/themes/vue/source/js/common.js#L282-L297
|
|
if (!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;
|
|
const top = isInView ? wrap.scrollTop : notThan ? curOffset : cur - height;
|
|
|
|
sidebar.scrollTop = top;
|
|
}
|
|
}
|
|
|
|
function getNavKey(path, id) {
|
|
return `${path}?id=${id}`;
|
|
}
|
|
|
|
export function scrollActiveSidebar(router) {
|
|
const cover = dom.find('.cover.show');
|
|
coverHeight = cover ? cover.offsetHeight : 0;
|
|
|
|
const sidebar = dom.getNode('.sidebar');
|
|
let lis = [];
|
|
if (sidebar !== null && sidebar !== undefined) {
|
|
lis = dom.findAll(sidebar, 'li');
|
|
}
|
|
|
|
for (let i = 0, len = lis.length; i < len; i += 1) {
|
|
const li = lis[i];
|
|
const a = li.querySelector('a');
|
|
if (!a) {
|
|
continue;
|
|
}
|
|
|
|
let href = a.getAttribute('href');
|
|
|
|
if (href !== '/') {
|
|
const {
|
|
query: { id },
|
|
path,
|
|
} = router.parse(href);
|
|
if (id) {
|
|
href = getNavKey(path, id);
|
|
}
|
|
}
|
|
|
|
if (href) {
|
|
nav[decodeURIComponent(href)] = li;
|
|
}
|
|
}
|
|
|
|
if (isMobile) {
|
|
return;
|
|
}
|
|
|
|
const path = router.getCurrentPath();
|
|
dom.off('scroll', () => highlight(path));
|
|
dom.on('scroll', () => highlight(path));
|
|
dom.on(sidebar, 'mouseover', () => {
|
|
hoverOver = true;
|
|
});
|
|
dom.on(sidebar, 'mouseleave', () => {
|
|
hoverOver = false;
|
|
});
|
|
}
|
|
|
|
export function scrollIntoView(path, id) {
|
|
if (!id) {
|
|
return;
|
|
}
|
|
const topMargin = config().topMargin;
|
|
const section = dom.find('#' + cssEscape(id));
|
|
section && scrollTo(section, topMargin);
|
|
|
|
const li = nav[getNavKey(path, id)];
|
|
const sidebar = dom.getNode('.sidebar');
|
|
const active = dom.find(sidebar, 'li.active');
|
|
active && active.classList.remove('active');
|
|
li && li.classList.add('active');
|
|
}
|
|
|
|
const scrollEl = dom.$.scrollingElement || dom.$.documentElement;
|
|
|
|
export function scroll2Top(offset = 0) {
|
|
scrollEl.scrollTop = offset === true ? 0 : Number(offset);
|
|
}
|