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

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

(问题是针对Firefox的。)

老丝达蒙2020/03/16 14:34:54

这将检查元素是否至少部分在视图中(垂直尺寸):

function inView(element) {
    var box = element.getBoundingClientRect();
    return inViewBox(box);
}

function inViewBox(box) {
    return ((box.bottom < 0) || (box.top > getWindowSize().h)) ? false : true;
}


function getWindowSize() {
    return { w: document.body.offsetWidth || document.documentElement.offsetWidth || window.innerWidth, h: document.body.offsetHeight || document.documentElement.offsetHeight || window.innerHeight}
}
猿Stafan2020/03/16 14:34:54

我使用此功能(因为大多数时候不需要x,所以它仅检查y是否在屏幕上)

function elementInViewport(el) {
    var elinfo = {
        "top":el.offsetTop,
        "height":el.offsetHeight,
    };

    if (elinfo.top + elinfo.height < window.pageYOffset || elinfo.top > window.pageYOffset + window.innerHeight) {
        return false;
    } else {
        return true;
    }

}
古一小宇宙2020/03/16 14:34:54

这里所有的答案都在确定该元素是否完全包含在视口中,而不仅仅是以某种方式可见。例如,如果视图底部仅可见图像的一半,则考虑到该“外部”,此处的解决方案将失败。

我有一个用例,其中我通过进行了延迟加载IntersectionObserver,但是由于在弹出过程中发生了动画,因此我不想观察页面加载时已经相交的任何图像为此,我使用了以下代码:

const bounding = el.getBoundingClientRect();
const isVisible = (0 < bounding.top && bounding.top < (window.innerHeight || document.documentElement.clientHeight)) ||
        (0 < bounding.bottom && bounding.bottom < (window.innerHeight || document.documentElement.clientHeight));

基本上,这是在检查视口中的上下边界是否独立。另一端可能在外面,但是只要一端在里面,它至少部分是“可见的”。

小卤蛋伽罗2020/03/16 14:34:54

这是一个告诉元素在元素的当前视口中是否可见的函数

function inParentViewport(el, pa) {
    if (typeof jQuery === "function"){
        if (el instanceof jQuery)
            el = el[0];
        if (pa instanceof jQuery)
            pa = pa[0];
    }

    var e = el.getBoundingClientRect();
    var p = pa.getBoundingClientRect();

    return (
        e.bottom >= p.top &&
        e.right >= p.left &&
        e.top <= p.bottom &&
        e.left <= p.right
    );
}
小卤蛋Tony2020/03/16 14:34:54

更好的解决方案:

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;
}
逆天西里2020/03/16 14:34:54

IMO尽可能简单:

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  return Math.abs(coords.top) <= coords.height;
}
逆天神无2020/03/16 14:34:54

作为Element.getBoundingClientRect()的支持,最简单的解决方案变得完美

function isInView(el) {
    let box = el.getBoundingClientRect();
    return box.top < window.innerHeight && box.bottom >= 0;
}
小哥猪猪2020/03/16 14:34:54

在Android上放大Google Chrome时,最不能接受的答案无效。结合Dan的answer,要在Android 上使用Chrome,必须使用visualViewport以下示例仅考虑垂直检查,并使用jQuery作为窗口高度:

var Rect = YOUR_ELEMENT.getBoundingClientRect();
var ElTop = Rect.top, ElBottom = Rect.bottom;
var WindowHeight = $(window).height();
if(window.visualViewport) {
    ElTop -= window.visualViewport.offsetTop;
    ElBottom -= window.visualViewport.offsetTop;
    WindowHeight = window.visualViewport.height;
}
var WithinScreen = (ElTop >= 0 && ElBottom <= WindowHeight);
A村村2020/03/16 14:34:54

我认为这是一种更实用的方法。 Dan的答案在递归上下文中不起作用。

当您的元素位于其他可滚动div内时,此功能通过递归测试直到HTML标记的任何级别来解决该问题,并在第一个false处停止。

/**
 * fullVisible=true only returns true if the all object rect is visible
 */
function isReallyVisible(el, fullVisible) {
    if ( el.tagName == "HTML" )
            return true;
    var parentRect=el.parentNode.getBoundingClientRect();
    var rect = arguments[2] || el.getBoundingClientRect();
    return (
            ( fullVisible ? rect.top    >= parentRect.top    : rect.bottom > parentRect.top ) &&
            ( fullVisible ? rect.left   >= parentRect.left   : rect.right  > parentRect.left ) &&
            ( fullVisible ? rect.bottom <= parentRect.bottom : rect.top    < parentRect.bottom ) &&
            ( fullVisible ? rect.right  <= parentRect.right  : rect.left   < parentRect.right ) &&
            isReallyVisible(el.parentNode, fullVisible, rect)
    );
};
村村L2020/03/16 14:34:53

我在这里遇到的所有答案仅检查该元素是否位于当前视口内但这并不意味着它是可见的
如果给定元素位于内容溢出的div内并且滚动到视图之外怎么办?

为了解决这个问题,您必须检查该元素是否包含在所有父元素中。
我的解决方案正是这样做的:

它还允许您指定必须可见的元素数量。

Element.prototype.isVisible = function(percentX, percentY){
    var tolerance = 0.01;   //needed because the rects returned by getBoundingClientRect provide the position up to 10 decimals
    if(percentX == null){
        percentX = 100;
    }
    if(percentY == null){
        percentY = 100;
    }

    var elementRect = this.getBoundingClientRect();
    var parentRects = [];
    var element = this;

    while(element.parentElement != null){
        parentRects.push(element.parentElement.getBoundingClientRect());
        element = element.parentElement;
    }

    var visibleInAllParents = parentRects.every(function(parentRect){
        var visiblePixelX = Math.min(elementRect.right, parentRect.right) - Math.max(elementRect.left, parentRect.left);
        var visiblePixelY = Math.min(elementRect.bottom, parentRect.bottom) - Math.max(elementRect.top, parentRect.top);
        var visiblePercentageX = visiblePixelX / elementRect.width * 100;
        var visiblePercentageY = visiblePixelY / elementRect.height * 100;
        return visiblePercentageX + tolerance > percentX && visiblePercentageY + tolerance > percentY;
    });
    return visibleInAllParents;
};

该解决方案忽略了由于其他事实(例如)元素可能不可见的事实opacity: 0

我已经在Chrome和Internet Explorer 11中测试了此解决方案。

米亚西门2020/03/16 14:34:53

我的简短快速版本:

function isElementOutViewport(el){
    var rect = el.getBoundingClientRect();
    return rect.bottom < 0 || rect.right < 0 || rect.left > window.innerWidth || rect.top > window.innerHeight;
}

和所需的jsFiddle:https ://jsfiddle.net/on1g619L/1/

泡芙猴子2020/03/16 14:34:53

有一个叫做inview的jQuery插件可以完成这项工作。

Pro老丝2020/03/16 14:34:53

我尝试了丹的答案但是,用于确定边界的代数意味着该元素必须既≤视口大小,又必须完全在视口内部才能获取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() */;
}
村村十三阳光2020/03/16 14:34:53

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

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

}

true如果元素的任何部分在视口中,则返回

猿伽罗2020/03/16 14:34:53

更新:时间在前进,我们的浏览器也在前进。不再推荐使用此技术,如果不需要支持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
  );
}