export function getScrollParent(el?: HTMLElement, includeHidden = false) { while (el) { if (includeHidden ? isPotentiallyScrollable(el) : hasScrollbar(el)) return el el = el.parentElement! } return document.scrollingElement as HTMLElement } export function getScrollParents(el?: Element | null, stopAt?: Element | null) { const elements: HTMLElement[] = [] if (stopAt && el && !stopAt.contains(el)) return elements while (el) { if (hasScrollbar(el)) elements.push(el as HTMLElement) if (el === stopAt) break el = el.parentElement! } return elements } export function hasScrollbar(el?: Element | null) { if (!el || el.nodeType !== Node.ELEMENT_NODE) return false const style = window.getComputedStyle(el) return style.overflowY === 'scroll' || (style.overflowY === 'auto' && el.scrollHeight > el.clientHeight) } function isPotentiallyScrollable(el?: Element | null) { if (!el || el.nodeType !== Node.ELEMENT_NODE) return false const style = window.getComputedStyle(el) return ['scroll', 'auto'].includes(style.overflowY) } export function getScrollBarWidth() { const outer = document.createElement('div'); outer.style.overflow = 'scroll'; outer.style.height = '200px'; outer.style.width = '100px'; outer.style.position = 'fixed' outer.style.opacity = '0' outer.style.pointerEvents = 'none' document.body.appendChild(outer); const inner = document.createElement('div'); inner.style.width = '100%'; outer.appendChild(inner); const widthNoScroll = outer.offsetWidth; const widthWithScroll = inner.offsetWidth; outer.remove() return widthNoScroll - widthWithScroll; }