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

(这个问题指的是Firefox。)


当前回答

新的交集观察者API非常直接地解决了这个问题。

这个解决方案将需要一个polyfill,因为Safari, Opera和Internet Explorer还不支持这个(polyfill包含在解决方案中)。

在这个解决方案中,在视图外有一个框,即目标(观察到的)。当它进入视图时,头部顶部的按钮是隐藏的。一旦框离开视图,就会显示它。

const buttonToHide = document.querySelector('button'); const hideWhenBoxInView = new IntersectionObserver((entries) => { if (entries[0].intersectionRatio <= 0) { // If not in view buttonToHide.style.display = "inherit"; } else { buttonToHide.style.display = "none"; } }); hideWhenBoxInView.observe(document.getElementById('box')); header { position: fixed; top: 0; width: 100vw; height: 30px; background-color: lightgreen; } .wrapper { position: relative; margin-top: 600px; } #box { position: relative; left: 175px; width: 150px; height: 135px; background-color: lightblue; border: 2px solid; } <script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script> <header> <button>NAVIGATION BUTTON TO HIDE</button> </header> <div class="wrapper"> <div id="box"> </div> </div>

其他回答

我的更短更快的版本:

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/

查看verge的源代码,它使用getBoundingClientRect。这就像:

function inViewport (element) {
  if (!element) return false;
  if (1 !== element.nodeType) return false;

  var html = document.documentElement;
  var rect = element.getBoundingClientRect();

  return !!rect &&
    rect.bottom >= 0 &&
    rect.right >= 0 && 
    rect.left <= html.clientWidth &&
    rect.top <= html.clientHeight;
}

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

/**
 * Returns Element placement information in Viewport
 * @link https://stackoverflow.com/a/70476497/2453148
 *
 * @typedef {object} ViewportInfo - Whether the element is…
 * @property {boolean} isInViewport - fully or partially in the viewport
 * @property {boolean} isPartiallyInViewport - partially in the viewport
 * @property {boolean} isInsideViewport - fully inside viewport
 * @property {boolean} isAroundViewport - completely covers the viewport
 * @property {boolean} isOnEdge - intersects the edge of viewport
 * @property {boolean} isOnTopEdge - intersects the top edge
 * @property {boolean} isOnRightEdge - intersects the right edge
 * @property {boolean} isOnBottomEdge - is intersects the bottom edge
 * @property {boolean} isOnLeftEdge - is intersects the left edge
 *
 * @param el Element
 * @return {Object} ViewportInfo
 */
function getElementViewportInfo(el) {

    let result = {};

    let rect = el.getBoundingClientRect();
    let windowHeight = window.innerHeight || document.documentElement.clientHeight;
    let windowWidth  = window.innerWidth || document.documentElement.clientWidth;

    let insideX = rect.left >= 0 && rect.left + rect.width <= windowWidth;
    let insideY = rect.top >= 0 && rect.top + rect.height <= windowHeight;

    result.isInsideViewport = insideX && insideY;

    let aroundX = rect.left < 0 && rect.left + rect.width > windowWidth;
    let aroundY = rect.top < 0 && rect.top + rect.height > windowHeight;

    result.isAroundViewport = aroundX && aroundY;

    let onTop    = rect.top < 0 && rect.top + rect.height > 0;
    let onRight  = rect.left < windowWidth && rect.left + rect.width > windowWidth;
    let onLeft   = rect.left < 0 && rect.left + rect.width > 0;
    let onBottom = rect.top < windowHeight && rect.top + rect.height > windowHeight;

    let onY = insideY || aroundY || onTop || onBottom;
    let onX = insideX || aroundX || onLeft || onRight;

    result.isOnTopEdge    = onTop && onX;
    result.isOnRightEdge  = onRight && onY;
    result.isOnBottomEdge = onBottom && onX;
    result.isOnLeftEdge   = onLeft && onY;

    result.isOnEdge = result.isOnLeftEdge || result.isOnRightEdge ||
        result.isOnTopEdge || result.isOnBottomEdge;

    let isInX =
        insideX || aroundX || result.isOnLeftEdge || result.isOnRightEdge;
    let isInY =
        insideY || aroundY || result.isOnTopEdge || result.isOnBottomEdge;

    result.isInViewport = isInX && isInY;

    result.isPartiallyInViewport =
        result.isInViewport && result.isOnEdge;

    return result;
}

在Android上放大谷歌Chrome浏览器时,最被接受的答案是不工作的。结合Dan的回答,要考虑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);

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

此函数通过递归测试HTML标记之前的任何级别,并在第一个false处停止,解决了当您的元素位于其他可滚动div中的问题。

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