findScrollableParent
import { findScrollableParent } from 'cx/util'; Copied The findScrollableParent function finds the nearest ancestor element that has scrollable overflow.
Basic Usage
import { findScrollableParent } from "cx/util";
const element = document.querySelector(".item");
// Find vertically scrollable parent
const scrollParent = findScrollableParent(element);
// Find horizontally scrollable parent
const hScrollParent = findScrollableParent(element, true);
How It Works
The function traverses up the DOM tree looking for an element where:
clientHeight < scrollHeight(for vertical) orclientWidth < scrollWidth(for horizontal)- The computed
overflow-yoroverflow-xis"auto"or"scroll"
If no scrollable parent is found, it returns the document’s scrolling element.
Examples
Detecting Scroll Container
import { findScrollableParent } from "cx/util";
function getScrollContainer(element: Element): HTMLElement {
return findScrollableParent(element) || document.documentElement;
}
Manual Scroll Control
import { findScrollableParent } from "cx/util";
function scrollToTop(element: Element): void {
const scrollParent = findScrollableParent(element);
if (scrollParent) {
scrollParent.scrollTop = 0;
}
}
function scrollToBottom(element: Element): void {
const scrollParent = findScrollableParent(element);
if (scrollParent) {
scrollParent.scrollTop = scrollParent.scrollHeight;
}
}
Infinite Scroll Detection
import { findScrollableParent } from "cx/util";
function setupInfiniteScroll(
container: Element,
loadMore: () => void
): () => void {
const scrollParent = findScrollableParent(container);
if (!scrollParent) return () => {};
function handleScroll() {
const { scrollTop, scrollHeight, clientHeight } = scrollParent;
const nearBottom = scrollTop + clientHeight >= scrollHeight - 100;
if (nearBottom) {
loadMore();
}
}
scrollParent.addEventListener("scroll", handleScroll);
return () => scrollParent.removeEventListener("scroll", handleScroll);
}
Horizontal Scrolling
import { findScrollableParent } from "cx/util";
function scrollHorizontally(element: Element, delta: number): void {
const scrollParent = findScrollableParent(element, true);
if (scrollParent) {
scrollParent.scrollLeft += delta;
}
}
Common Use Cases
Dropdown Positioning
import { findScrollableParent } from "cx/util";
function positionDropdown(trigger: Element, dropdown: HTMLElement): void {
const scrollParent = findScrollableParent(trigger);
const triggerRect = trigger.getBoundingClientRect();
const scrollRect = scrollParent?.getBoundingClientRect();
// Check if there's room below
const spaceBelow = scrollRect
? scrollRect.bottom - triggerRect.bottom
: window.innerHeight - triggerRect.bottom;
if (spaceBelow < dropdown.offsetHeight) {
// Position above instead
dropdown.style.bottom = `${triggerRect.height}px`;
}
}
Scroll Event Binding
import { findScrollableParent } from "cx/util";
function onScroll(element: Element, callback: () => void): () => void {
const scrollParent = findScrollableParent(element);
if (!scrollParent) return () => {};
scrollParent.addEventListener("scroll", callback);
return () => scrollParent.removeEventListener("scroll", callback);
}
API
function findScrollableParent(
sourceEl: Element,
horizontal?: boolean
): HTMLElement | null;
| Parameter | Type | Default | Description |
|---|---|---|---|
| sourceEl | Element | - | The element to start searching from |
| horizontal | boolean | false | Search for horizontal scroll instead of vertical |
Returns: The nearest scrollable ancestor, or the document’s scrolling element if none found.