type ScrollElement = HTMLElement | Window; function isWindow(val: unknown): val is Window { return val === window; } // get nearest scroll element // http://w3help.org/zh-cn/causes/SD9013 // http://stackoverflow.com/questions/17016740/onscroll-function-is-not-working-for-chrome const overflowScrollReg = /scroll|auto/i; export function getScroller(el: HTMLElement, root: ScrollElement = window) { let node = el; while ( node && node.tagName !== 'HTML' && node.nodeType === 1 && node !== root ) { const { overflowY } = window.getComputedStyle(node); if (overflowScrollReg.test(overflowY)) { if (node.tagName !== 'BODY') { return node; } // see: https://github.com/youzan/vant/issues/3823 const { overflowY: htmlOverflowY } = window.getComputedStyle( node.parentNode as Element ); if (overflowScrollReg.test(htmlOverflowY)) { return node; } } node = node.parentNode as HTMLElement; } return root; } export function getScrollTop(el: ScrollElement): number { return 'scrollTop' in el ? el.scrollTop : el.pageYOffset; } export function setScrollTop(el: ScrollElement, value: number) { if ('scrollTop' in el) { el.scrollTop = value; } else { el.scrollTo(el.scrollX, value); } } export function getRootScrollTop(): number { return ( window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 ); } export function setRootScrollTop(value: number) { setScrollTop(window, value); setScrollTop(document.body, value); } // get distance from element top to page top or scroller top export function getElementTop(el: ScrollElement, scroller?: HTMLElement) { if (isWindow(el)) { return 0; } const scrollTop = scroller ? getScrollTop(scroller) : getRootScrollTop(); return el.getBoundingClientRect().top + scrollTop; } export function getVisibleHeight(el: ScrollElement) { if (isWindow(el)) { return el.innerHeight; } return el.getBoundingClientRect().height; } export function getVisibleTop(el: ScrollElement) { if (isWindow(el)) { return 0; } return el.getBoundingClientRect().top; }