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

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

(问题是指Firefox) html


#1楼

请参阅使用getBoundingClientRect边缘源。 就像是: node

function inViewport (el) {

    var r, html;
    if ( !el || 1 !== el.nodeType ) { return false; }
    html = document.documentElement;
    r = el.getBoundingClientRect();

    return ( !!r 
      && r.bottom >= 0 
      && r.right >= 0 
      && r.top <= html.clientHeight 
      && r.left <= html.clientWidth 
    );

}

若是元素的任何部分在视口中,则返回truegit


#2楼

更新:时间在前进,咱们的浏览器也在前进。 再也不推荐使用此技术,若是不须要支持IE <7,则应使用下面的@Dan解决方案( https://stackoverflow.com/a/7557433/5628 )。 github

原始解决方案(现已过期): 浏览器

这将检查该元素在当前视口中是否彻底可见: dom

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

#3楼

更好的解决方案: 测试

function getViewportSize(w) {
    var w = w || window;
    if(w.innerWidth != null) return {w:w.innerWidth, h:w.innerHeight};
    var d = w.document;
    if (document.compatMode == "CSS1Compat") {
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    }
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}
function isViewportVisible(e) {
    var box = e.getBoundingClientRect();
    var height = box.height || (box.bottom - box.top);
    var width = box.width || (box.right - box.left);
    var viewport = getViewportSize();
    if(!height || !width) return false;
    if(box.top > viewport.h || box.bottom < 0) return false;
    if(box.right < 0 || box.left > viewport.w) return false;
    return true;    
}

#4楼

更新资料

在现代浏览器中,您可能想查看Intersection Observer API ,它具备如下优势: ui

  • 比收听滚动事件更好的性能
  • 适用于跨网域iframe
  • 能够判断一个元素是否正在阻碍/相交

Intersection Observer正在成为完善的标准,而且已在Chrome 51 +,Edge 15+和Firefox 55+中获得支持,而且正在为Safari开发。 还有一个polyfill可用。


先前的答案

Dan提供答案存在一些问题,可能使它不适用于某些状况。 他在底部的答案中指出了其中一些问题,即他的代码将对如下元素产生误报:

  • 被正在测试的元素前面的另外一个元素隐藏
  • 在父元素或祖先元素的可见区域以外
  • 使用CSS clip属性隐藏的元素或其子元素

这些限制在如下简单测试结果中获得了证实

使用isElementInViewport的测试失败

解决方案: 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/

结果:

使用isElementVisible经过测试

附加条款

可是,此方法并不是没有其自身的局限性。 例如,即便在前面的元素实际上没有隐藏其任何部分的状况下,使用比同一位置的另外一个元素更低的z-index测试的元素也将被标识为隐藏。 不过,这种方法在某些状况下仍有其用处,但Dan的解决方案却没法解决。

element.getBoundingClientRect()document.elementFromPoint()都是CSSOM工做草案规范的一部分,而且至少在IE 6和更高版本以及大多数台式机浏览器中都获得了长期支持(尽管并不是彻底如此)。 有关更多信息,请参见这些功能的Quirksmode

contains()用于查看document.elementFromPoint()返回的元素是不是咱们正在测试可见性的元素的子节点。 若是返回的元素是同一元素,则它也返回true。 这只会使检查更可靠。 全部主要浏览器均支持该功能,Firefox 9.0是它们最后添加的浏览器。 要得到较早的Firefox支持,请查看此答案的历史记录。

若是要在元素周围测试更多点的可见性(例如,确保元素覆盖率不超过50%),则无需花费太多时间来调整答案的最后一部分。 可是请注意,若是检查每一个像素以确保其100%可见,这可能会很是慢。


#5楼

我尝试了Dan的答案, 可是用于肯定范围的代数意味着该元素必须既≤视口大小,又必须彻底在视口内才能获得true ,很容易致使假阴性。 若是要肯定某个元素是否彻底在视口中,则ryanve的答案很接近,可是要测试的元素应与该视口重叠,所以请尝试如下操做:

function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();

    return rect.bottom > 0 &&
        rect.right > 0 &&
        rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ &&
        rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;
}
相关文章
相关标签/搜索