如何判断 DOM 元素在当前视口中是否可见?

2022-08-29 22:05:46

有没有一种有效的方法来判断DOM元素(在HTML文档中)当前是否可见(出现在视口中)?

(这个问题指的是Firefox。


答案 1

现在大多数浏览器都支持getBoundingClientRect方法,这已经成为最佳实践。使用旧答案非常慢,不准确,并且有几个错误

选择为正确的解决方案几乎从不精确


此解决方案在Internet Explorer 7(及更高版本),iOS 5(及更高版本)Safari,Android 2.0(Eclair)以及后来的BlackBerry,Opera Mobile和Internet Explorer Mobile 9上进行了测试。


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); // Internet Explorer 9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

如果您进行任何DOM修改,它们当然可以更改元素的可见性。

准则和常见陷阱:

也许您需要跟踪页面缩放/移动设备捏合?jQuery应该处理缩放/捏合跨浏览器,否则第一个第二个链接应该对你有所帮助。

如果修改 DOM,则会影响元素的可见性。您应该控制它并手动调用。很抱歉,我们没有任何跨浏览器事件。另一方面,它允许我们进行优化并仅对可以更改元素可见性的DOM修改执行重新检查。handler()onrepaint

永远不要只在jQuery $(document).ready()中使用它,因为目前没有保证CSS已经应用。您的代码可以在硬盘驱动器上与CSS一起本地工作,但是一旦放在远程服务器上,它就会失败。

触发后,将应用样式,但尚未加载图像。因此,我们应该添加事件侦听器。DOMContentLoadedwindow.onload

我们还无法捕捉缩放/捏合事件。

最后的手段可能是以下代码:

/* TODO: this looks like a very bad code */
setInterval(handler, 600);

您可以使用HTML5 API的精彩功能页面,如果您关心网页的选项卡是否处于活动状态且可见。

TODO:此方法不处理两种情况:

  • 使用 重叠。z-index
  • 在元素的容器中使用。overflow-scroll
  • 尝试一些新的东西 - 交叉观察者API解释

答案 2

更新:时间在流逝,我们的浏览器也是如此。不再建议使用此技术,如果您不需要支持 7 之前的 Internet Explorer 版本,则应使用 Dan 的解决方案。

原始解决方案(现已过时):

这将检查该元素在当前视口中是否完全可见:

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
  );
}