type ScrollElement = HTMLElement | Window; interface Margin { marginTop?: number; marginRight?: number; marginBottom?: number; marginLeft?: number; } export function isFixed(element: HTMLElement) { while ( element && element.tagName !== 'HTML' && element.tagName !== 'BODY' && element.nodeType === 1 ) { const { position } = window.getComputedStyle(element); if (position === 'fixed') { return true; } element = element.parentNode as HTMLElement; } return false; } export function getRect(element: HTMLElement): ClientRect; export function getRect(element: Window): { width: number; height: number }; export function getRect(element: ScrollElement = window) { if (element === window) { // http://stackoverflow.com/questions/1248081/get-the-browser-viewport-dimensions-with-javascript const doc = document.documentElement; return { width: Math.max(doc.clientWidth, window.innerWidth || 0), height: Math.max(doc.clientHeight, window.innerHeight || 0), }; } return (element as HTMLElement).getBoundingClientRect(); } export function getScroll(element: ScrollElement = window) { if ('scrollTop' in element) { return { top: element.scrollTop, left: element.scrollLeft, }; } return { top: window.pageYOffset, left: window.pageXOffset, }; } export function getPagePosition(element: HTMLElement) { const rect = getRect(element); let top; let left; if (document.scrollingElement) { top = document.scrollingElement.scrollTop; left = document.scrollingElement.scrollLeft; } else { top = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; left = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; } return { top: rect.top + top, bottom: rect.bottom + top, left: rect.left + left, right: rect.right + left, }; } export function isXVisible(element: HTMLElement, margin?: Margin) { const { width } = getRect(window); const { left, right } = getRect(element); const { marginLeft = 0, marginRight = 0 } = margin || {}; const windowLeft = -marginLeft; const windowRight = width + marginRight; return ( (left > windowLeft && left < windowRight) || (right > windowLeft && right < windowRight) ); } export function isYVisible(element: HTMLElement, margin?: Margin) { const { height } = getRect(window); const { top, bottom } = getRect(element); const { marginTop = 0, marginBottom = 0 } = margin || {}; const windowTop = -marginTop; const windowBottom = height + marginBottom; return ( (top > windowTop && top < windowBottom) || (bottom > windowTop && bottom < windowBottom) ); } export function isVisible(element: HTMLElement, margin?: Margin) { return isXVisible(element, margin) && isYVisible(element, margin); } export function findNearestElement( element: HTMLElement, targetTagName: keyof HTMLElementTagNameMap ) { while (element && element.tagName !== 'HTML' && element.tagName !== 'BODY') { if (element.tagName.toLowerCase() === targetTagName) { return element; } element = element.parentElement as HTMLElement; } return null; }