找到第一个可滚动的父级

在这种情况下,我需要将一个元素滚动到 viewport 中。问题是我不知道哪个元素是可滚动的。例如,在肖像中,主体是可滚动的,而在风景中,它是另一个元素(还有更多的情况会改变可滚动的元素)

现在的问题是,给定一个需要滚动到 viewport 中的元素,找到它的第一个可滚动父元素的最佳方法是什么?

我已经设置了一个演示 给你。通过按钮,你可以在两种不同的情况下切换

<div class="outer">
<div class="inner">
<div class="content">
...
<span>Scroll me into view</span>
</div>
</div>
</div>

主体是可滚动的或 .outer

有什么建议吗?

54779 次浏览

I think you want this.

$('button').click(function() {
$("body").addClass("body");
$('.outer').toggleClass('scroller');
check($(".content"));
});


function check(el) {
var overflowY = el.css("overflow-y");
if (overflowY == "scroll") {
alert(el.attr("class") + " has");
} else {
if(el.parent().length > 0)
check(el.parent());
else
return false;
}
}
body {
height: 450px;
overflow-y: scroll;
}


div.inner {
width: 200px;
height: 400px;
border: 1px solid #000;
}


div.outer {
width: 200px;
height: 200px;
}


div.outer.scroller {
overflow-y: scroll;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>
toggle
</button>
<div class="outer">
<div class="inner">
<div class="content">
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur
sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
</div>
</div>
</div>

Just check if the scrollbar is visible, if not look to the parent.

function getScrollParent(node) {
if (node == null) {
return null;
}


if (node.scrollHeight > node.clientHeight) {
return node;
} else {
return getScrollParent(node.parentNode);
}
}

If you are using jQuery UI you can use the scrollParent method. Have a look at the API or the source.

From the API:

.scrollParent(): Get the closest ancestor element that is scrollable

This method does not accept any arguments. This method finds the nearest ancestor that allows scrolling. In other words, the .scrollParent() method finds the element that the currently selected element will scroll within.

Note: This method only works on jQuery objects containing one element.

If you are not using jQuery UI but are using jQuery, then there are alternative independent libraries providing similar functionality, such as:

jquery-scrollparent

This is a pure JS port of the jQuery UI scrollParent method that cweston spoke of. I went with this rather than the accepted answer's solution which will not find the scroll parent if there's no content overflow yet.

The one difference with my port is that, if no parent is found with the right value for the CSS overflow property, I return the <body> element. JQuery UI, instead returned the document object. This is odd as values like .scrollTop can be retrieved from the <body> but not the document.

function getScrollParent(element, includeHidden) {
var style = getComputedStyle(element);
var excludeStaticParent = style.position === "absolute";
var overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;


if (style.position === "fixed") return document.body;
for (var parent = element; (parent = parent.parentElement);) {
style = getComputedStyle(parent);
if (excludeStaticParent && style.position === "static") {
continue;
}
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
}


return document.body;
}

the answer with most votes doesn't work in all cases scrollHeight > clientHeight can be true even if there is no scrollbar.

I found this gist solution https://github.com/olahol/scrollparent.js/blob/master/scrollparent.js#L13

^ total credit to https://github.com/olahol who wrote the code.

Refactored it to es6:

export const getScrollParent = (node) => {
const regex = /(auto|scroll)/;
const parents = (_node, ps) => {
if (_node.parentNode === null) { return ps; }
return parents(_node.parentNode, ps.concat([_node]));
};


const style = (_node, prop) => getComputedStyle(_node, null).getPropertyValue(prop);
const overflow = _node => style(_node, 'overflow') + style(_node, 'overflow-y') + style(_node, 'overflow-x');
const scroll = _node => regex.test(overflow(_node));


/* eslint-disable consistent-return */
const scrollParent = (_node) => {
if (!(_node instanceof HTMLElement || _node instanceof SVGElement)) {
return;
}


const ps = parents(_node.parentNode, []);


for (let i = 0; i < ps.length; i += 1) {
if (scroll(ps[i])) {
return ps[i];
}
}


return document.scrollingElement || document.documentElement;
};


return scrollParent(node);
/* eslint-enable consistent-return */
};

you can use it like:

const $yourElement = document.querySelector('.your-class-or-selector');
getScrollParent($yourElement);

Building upon further on the @Web_Designer's answer,

If you are passing the jQuery object for that element and are getting the following error,

Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'

Then try passing just the Dom Node element which btw resides at array key 0 if the element is a single element. Eg.

getScrollParent(jQuery("#" + formid)[0])

Using google chrome dev tools, when you've scrolled partially down the page, inspect the page, select the DOM node that you think might be the one that is being scrolled. Then pull up the console (hit ESC from within the Elements tab of the dev tools) and type $0.scrollTop. This will print out the current scroll position of that element. If it is NOT 0 then you will know that that is the element that is being scrolled.

This is the way to find what you want.

document.addEventListener('scroll', (e) => {
console.log(e.target.scrollingElement.scrollTop);
})

this is what I have so-far that seems to work across web component custom element shadowRoots; we might expand it further to add a scroll event handler once scrolling starts to unload this handling and track a specific node's scroll event

function scroller(node){ return node.scrollTop }
function handler(event){
const path = event.composedPath();
const scrollNode = path.find(scroller);
const scrollNodes = path.filter(scroller);
console.warn('scroll!',{scrollNode, scrollNodes});
}
window.addEventListener('mousewheel', handler, {capture: true});
window.addEventListener('keydown', handler, {capture: true});