Explain Codes LogoExplain Codes Logo

Find first scrollable parent

javascript
responsive-design
promises
callbacks
Anton ShumikhinbyAnton Shumikhin·Jan 14, 2025
TLDR

This JavaScript function can be used for detecting the first parent element of a specified element that permits scrolling:

function getScrollableParent(node) { // Let's be Sherlock of web page and find the needed scroll element. const isScrollable = n => /^(auto|scroll)$/.test(window.getComputedStyle(n)['overflow-y']); while (node && node !== document.body && !isScrollable(node)) { node = node.parentElement; // Stairway to... parent! } return node || document.body; // Either parent or "Master of Puppets" (document body). } const scrollableParent = getScrollableParent(element);

Call getScrollableParent(element), replacing element with your targeted element, for it to traverse up to the DOM and return the first element that allows vertical scrolling. If no such parent exists, you will be gifted with null.

Understanding the function

Let's dissect this function to better understand how it identifies the scrollable parent.

Detecting scroll with computed styles

The function checks the computed style overflow-y of every parent until it finds one with a value of either 'auto' or 'scroll', indicating a vertical scroll. It leverages window.getComputedStyle(n)['overflow-y'] to fetch this data.

Moving up the DOM

The function ascends the DOM tree via node.parentElement, switching to each parent element in succession. The process is likened to climbing a ladder where each rung is a parent situated higher in the DOM tree.

The body is the fallback

If it doesn't find an explicit scrollable parent, our trusty function defaults to document.body. This is because, in some scenarios like fixed-position elements, document.body acts as the scrollable container.

Elevating the solution with enhancements

Orientation aware solution

This extended solution is brilliant for responsive web designs, as they often change the scrollable element with orientation changes by re-adjusting the layout.

window.addEventListener('resize', function() { // Element won't change orientation but we got to keep up with world(or screen) change! const scrollableParent = getScrollableParent(element); });

Here, the function is triggered whenever the window is resized—including when the orientation is switched from portrait to landscape or vice versa.

Overflow X and Y

Our solution considers only vertical (y-axis) scrolling. However, if you wish to include horizontal scrolling, modify the regular expression as follows:

const isScrollable = n => /^(auto|scroll|overlay)$/.test(window.getComputedStyle(n).overflowY) || // vertical sherlock /^(auto|scroll|overlay)$/.test(window.getComputedStyle(n).overflowX); // horizontal watson

Syntax refactoring

Refactoring the function for ES6 syntax makes it cleaner and more maintainable.

const getScrollableParent = (node) => { // clear and neat, like a perfect code diet. const { body } = document; // a little destructure never hurt anyone, right? const isScrollable = ({ overflowY, overflowX }) => /^(auto|scroll|overlay)$/.test(overflowY) || /^(auto|scroll|overlay)$/.test(overflowX); // even the birds (tweets) love this. while (node && node !== body && !isScrollable(window.getComputedStyle(node))) { // recursion, it's like going in circles, but upwards. node = node.parentElement; } return node || body; // body or not, here we come! };