type ScrollElement = HTMLElement | Window; function isWindow(val: unknown): val is Window { return val === window; } export function getScrollTop(el: ScrollElement): number { const top = 'scrollTop' in el ? el.scrollTop : el.pageYOffset; // iOS scroll bounce cause minus scrollTop return Math.max(top, 0); } 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; }