如何判断一个DOM元素在当前视口中是否可见?
有没有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中 )?
(关于Firefox的问题)
更新:时间流逝,所以我们的浏览器。 此技术不再推荐 ,如果您不需要支持IE <7,则应使用下面的@ Dan解决方案(https://stackoverflow.com/a/7557433/5628)。
原始解决方案(现在已过时):
这将检查元素在当前视口中是否完全可见:
function elementInViewport(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top >= window.pageYOffset &&
left >= window.pageXOffset &&
(top + height) <= (window.pageYOffset + window.innerHeight) &&
(left + width) <= (window.pageXOffset + window.innerWidth)
);
}
您可以简单地修改它以确定元素的任何部分是否在视口中可见:
function elementInViewport2(el) {
var top = el.offsetTop;
var left = el.offsetLeft;
var width = el.offsetWidth;
var height = el.offsetHeight;
while(el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
left += el.offsetLeft;
}
return (
top < (window.pageYOffset + window.innerHeight) &&
left < (window.pageXOffset + window.innerWidth) &&
(top + height) > window.pageYOffset &&
(left + width) > window.pageXOffset
);
}
现在大多数浏览器都支持getBoundingClientRect方法,这已经成为最佳实践。 使用旧的答案很慢,不准确,并且有一些错误。
选择正确的解决方案几乎从不精确。 你可以阅读更多关于它的错误。
此解决方案已在IE7 +,iOS5 + Safari,Android2 +,Blackberry,Opera Mobile和IE Mobile 10上进行测试。
function isElementInViewport (el) {
//special bonus for those using jQuery
if (typeof jQuery === "function" && el instanceof jQuery) {
el = el[0];
}
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
}
如何使用:
你可以确定上面给出的函数在被调用的时候返回正确的答案,但是跟踪元素的可见性作为一个事件呢?
将以下代码放置在<body>
标记的底部:
function onVisibilityChange(el, callback) {
var old_visible;
return function () {
var visible = isElementInViewport(el);
if (visible != old_visible) {
old_visible = visible;
if (typeof callback == 'function') {
callback();
}
}
}
}
var handler = onVisibilityChange(el, function() {
/* your code go here */
});
//jQuery
$(window).on('DOMContentLoaded load resize scroll', handler);
/* //non-jQuery
if (window.addEventListener) {
addEventListener('DOMContentLoaded', handler, false);
addEventListener('load', handler, false);
addEventListener('scroll', handler, false);
addEventListener('resize', handler, false);
} else if (window.attachEvent) {
attachEvent('onDOMContentLoaded', handler); // IE9+ :(
attachEvent('onload', handler);
attachEvent('onscroll', handler);
attachEvent('onresize', handler);
}
*/
如果你做任何DOM修改,他们当然可以改变你的元素的可见性。
指南和常见陷阱:
也许你需要跟踪页面缩放/移动设备捏? jQuery应该处理缩放/捏交叉浏览器,否则第一或第二个链接应该会对你有帮助。
如果修改DOM ,它可能会影响元素的可见性。 你应该控制它并手动调用handler()
。 不幸的是,我们没有跨浏览器的onrepaint
事件。 另一方面,它允许我们进行优化并仅对可能会更改元素可见性的DOM修改进行重新检查。
永远不要在jQuery $(document).ready()中使用它,因为在这一刻没有应用保证CSS。 你的代码可以在硬盘上用你的CSS本地工作,但是一旦放在远程服务器上就会失败。
在DOMContentLoaded
被激发之后,将应用样式,但图像尚未加载。 所以,我们应该添加window.onload
事件监听器。
我们无法捕捉缩放/捏事件。
最后的手段可能是以下代码:
/* TODO: this looks like a very bad code */
setInterval(handler, 600);
如果您关心网页上的选项卡是否处于活动状态并可见,则可以使用超棒的功能pageVisibiliy HTML5 API。
TODO:这种方法不能处理两种情况:
z-index
重叠 在元素的容器中使用overflow-scroll
尝试新的东西https://pawelgrzybek.com/the-intersection-observer-api-explained/
更新
在现代浏览器中,您可能需要查看Intersection Observer API,它具有以下优点:
Intersection Observer正在成为一个完整的标准,并已在Chrome 51 +,Edge 15+和Firefox 55+中得到支持,并且正在为Safari开发。 还有一个polyfill可用。
先前的回答
Dan提供的答案存在一些问题,可能会使其成为某些情况下不适用的方法。 他在底部附近的回答中指出了其中的一些问题,他的代码会给出下列元素的误报:
clip
属性隐藏的元素或其子元素 这些限制在以下简单测试结果中得到证明:
解决方案: isElementVisible()
下面是这些问题的解决方案,下面的测试结果和代码的一些部分的解释。
function isElementVisible(el) {
var rect = el.getBoundingClientRect(),
vWidth = window.innerWidth || doc.documentElement.clientWidth,
vHeight = window.innerHeight || doc.documentElement.clientHeight,
efp = function (x, y) { return document.elementFromPoint(x, y) };
// Return false if it's not in the viewport
if (rect.right < 0 || rect.bottom < 0
|| rect.left > vWidth || rect.top > vHeight)
return false;
// Return true if any of its four corners are visible
return (
el.contains(efp(rect.left, rect.top))
|| el.contains(efp(rect.right, rect.top))
|| el.contains(efp(rect.right, rect.bottom))
|| el.contains(efp(rect.left, rect.bottom))
);
}
通过测试: http : //jsfiddle.net/AndyE/cAY8c/
结果是:
补充笔记
然而,这种方法并非没有其自身的局限性。 例如,即使前面的元素实际上并未隐藏其中的任何部分,在同一位置使用比其他元素更低的z索引进行测试的元素也将被标识为隐藏。 尽管如此,这种方法在Dan的解决方案没有涵盖的某些情况下有其用处。
element.getBoundingClientRect()
和document.elementFromPoint()
都是CSSOM工作草案规范的一部分,至少在IE 6及更高版本和大多数桌面浏览器中都支持很长时间(尽管并非完美)。 有关这些功能的更多信息,请参阅Quirksmode。
contains()
用于查看document.elementFromPoint()
返回的元素是否是我们要测试可见性的元素的子节点。 如果返回的元素是相同的元素,它也返回true。 这只是使检查更健壮。 它在所有主流浏览器都支持,Firefox 9.0是最后一个添加它的地方。 对于较老的Firefox支持,请查看此答案的历史记录。
如果你想测试元素周围的更多点以获取可视性,即确保元素覆盖不超过50%,那么调整答案的最后部分不会花太多时间。 但是,请注意,如果您检查每个像素以确保其100%可见,则它可能会非常缓慢。
链接地址: http://www.djcxy.com/p/14601.html上一篇: How to tell if a DOM element is visible in the current viewport?