docsify/src/event.js
2017-02-09 21:24:14 +08:00

155 lines
4.2 KiB
JavaScript

import { isMobile } from './util'
/**
* Active sidebar when scroll
* @link https://buble.surge.sh/
*/
export function scrollActiveSidebar () {
if (isMobile()) return
let hoveredOverSidebar = false
const anchors = document.querySelectorAll('.anchor')
const sidebar = document.querySelector('.sidebar')
const sidebarHeight = sidebar.clientHeight
const nav = {}
const lis = sidebar.querySelectorAll('li')
let active = sidebar.querySelector('li.active')
for (let i = 0, len = lis.length; i < len; i += 1) {
const li = lis[i]
let href = li.querySelector('a').getAttribute('href')
if (href !== '/') {
const match = href.match('#([^#]+)$')
if (match && match.length) href = match[0].slice(1)
}
nav[decodeURIComponent(href)] = li
}
function highlight () {
const top = document.body.scrollTop
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[last.getAttribute('data-id')]
if (!li || li === active) return
if (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 (!hoveredOverSidebar && !sticky.noSticky) {
const currentPageOffset = 0
const currentActiveOffset = active.offsetTop + active.clientHeight + 40
const currentActiveIsInView = (
active.offsetTop >= sidebar.scrollTop &&
currentActiveOffset <= sidebar.scrollTop + sidebarHeight
)
const linkNotFurtherThanSidebarHeight = currentActiveOffset - currentPageOffset < sidebarHeight
const newScrollTop = currentActiveIsInView
? sidebar.scrollTop
: linkNotFurtherThanSidebarHeight
? currentPageOffset
: currentActiveOffset - sidebarHeight
sidebar.scrollTop = newScrollTop
}
}
window.removeEventListener('scroll', highlight)
window.addEventListener('scroll', highlight)
sidebar.addEventListener('mouseover', () => { hoveredOverSidebar = true })
sidebar.addEventListener('mouseleave', () => { hoveredOverSidebar = false })
}
export function scrollIntoView () {
const id = window.location.hash.match(/#[^#\/]+$/g)
if (!id || !id.length) return
const section = document.querySelector(decodeURIComponent(id[0]))
if (section) setTimeout(() => section.scrollIntoView(), 0)
return section
}
/**
* Acitve link
*/
export function activeLink (dom, activeParent) {
const host = window.location.href
dom = typeof dom === 'object' ? dom : document.querySelector(dom)
if (!dom) return
let target
;[].slice.call(dom.querySelectorAll('a'))
.sort((a, b) => b.href.length - a.href.length)
.forEach(node => {
if (host.indexOf(node.href) === 0 && !target) {
activeParent
? node.parentNode.classList.add('active')
: node.classList.add('active')
target = node
} else {
activeParent
? node.parentNode.classList.remove('active')
: node.classList.remove('active')
}
})
return target
}
/**
* sidebar toggle
*/
export function bindToggle (dom) {
dom = typeof dom === 'object' ? dom : document.querySelector(dom)
if (!dom) return
const body = document.body
dom.addEventListener('click', () => body.classList.toggle('close'))
if (isMobile()) {
const sidebar = document.querySelector('.sidebar div')
sidebar.addEventListener('click', () => {
body.classList.toggle('close')
setTimeout(() => activeLink(sidebar, true), 0)
})
}
}
export function scroll2Top (offset = 0) {
document.body.scrollTop = offset === true ? 0 : Number(offset)
}
export function sticky () {
sticky.dom = sticky.dom || document.querySelector('section.cover')
const coverHeight = sticky.dom.getBoundingClientRect().height
return (function () {
if (window.pageYOffset >= coverHeight || sticky.dom.classList.contains('hidden')) {
document.body.classList.add('sticky')
sticky.noSticky = false
} else {
document.body.classList.remove('sticky')
sticky.noSticky = true
}
})()
}