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

ben*_*ita 878 html javascript browser firefox dom

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

(关于Firefox的问题)

Dan*_*Dan 1338

现在大多数浏览器都支持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() */
    );
}
Run Code Online (Sandbox Code Playgroud)

如何使用:

您可以确定上面给出的函数在调用时返回正确的答案,但是跟踪元素作为事件的可见性呢?

将以下代码放在代码的底部<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);
}
*/
Run Code Online (Sandbox Code Playgroud)

如果您进行任何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); 
Run Code Online (Sandbox Code Playgroud)

如果您关心网页的标签是否有效且可见,您可以使用精彩的功能页面Visibiliy HTML5 API.

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

  • 计算假设元素小于屏幕.如果你有高或宽的元素,使用`return(rect.bottom> = 0 && rect.right> = 0 && rect.top <=(window.innerHeight || document.documentElement.clientHeight)&&可能更准确rect.left <=(window.innerWidth || document.documentElement.clientWidth));` (61认同)
  • 提示:对于那些试图用jQuery实现这一点的人,只需要传递HTML DOM对象的友好提示(例如,`isElementInViewport(document.getElementById('elem'))`)而不是jQuery对象(例如,`isElementInViewport( $("#elem))`).jQuery等价于添加`[0]`,如:`isElementInViewport($("#elem)[0])`. (11认同)
  • `el没有定义` (10认同)
  • 我正在使用这个解决方案(但要注意"botom"错字).当我们考虑的元素将图像放入其中时,还有一些需要注意的事项.Chrome(至少)必须等待加载图像才能获得boundingRectangle的确切值.似乎Firefox没有这个"问题" (5认同)
  • 在体内容器中启用滚动时是否有效.例如,它在这里不起作用 - http://agaase.github.io/webpages/demo/isonscreen2.html isElementInViewport(document.getElementById("innerele")).innerele存在于启用了滚动的容器内. (4认同)
  • 此答案检查元素是否完全显示在视口中。如果它的一部分被截断,则将其视为未显示。如果您想知道元素的任何部分是否可见,请使用@Roonaan 评论中的片段 (3认同)
  • 如果您不需要支持IE 5.5及更低版本(您可能不支持),则可以使用`(window.innerWidth || document.documentElement.clientWidth)`以及跨浏览器解决方案的高度等效项. (2认同)
  • 这里是我的版本,支持的容器元素(例如,如果该元素是另外一个滚动的元素之内),全部或部分的可见性(jQuery的只有尽管):https://gist.github.com/anonymous/f09b5cd487b5ad55c46e (2认同)
  • 需要澄清这个代数。它更像是测试一个元素是否_完全_位于视口之外。对于仅部分位于视口之外的元素,它返回“不可见”。请参阅@Wolf 答案以获得更直观的版本。(来自“isElementInViewport”)。事件监听效果很好。 (2认同)

Pre*_*aul 329

更新:时间推进,我们的浏览器也是如此.如果您不需要支持IE <7,则不再推荐使用此技术,您应该使用@ Dan的解决方案(/sf/answers/529020341/).

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

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

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)
  );
}
Run Code Online (Sandbox Code Playgroud)

您可以简单地修改它以确定元素的任何部分是否在视口中可见:

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
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 如果元素存在于可滚动的div中并滚动出视图,该怎么办? (12认同)
  • 请查看下面的较新版本的脚本 (2认同)

And*_*y E 160

更新

在现代浏览器中,您可能需要查看Intersection Observer API,它提供以下好处:

  • 比收听滚动事件更好的性能
  • 适用于跨域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))
    );
}
Run Code Online (Sandbox Code Playgroud)

通过测试: 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%可见,则可能会非常慢.

  • 如果你有圆角或应用了变换,检查元素的中心是否可见也是有益的,因为边界角可能不会返回预期的元素:`element.contains(efp(rect.right - (rect. width/2),rect.bottom - (rect.height/2)))` (9认同)
  • 您是要使用doc.documentElement.clientWidth吗?应该是“ document.documentElement”吗?另外,这是唯一的方法,也适用于用例,例如使用CSS'clip'属性隐藏元素的内容以实现可访问性:http://snook.ca/archives/html_and_css/hiding-content-for -可访问性 (2认同)
  • 对我来说,这是行不通的。但是先前答案中的inViewport()在FF中有效。 (2认同)
  • 对我来说不起作用(Chrome Canary 50)。不知道为什么,也许是本地的圆角?我不得不稍微降低坐标才能使其工作el.contains(efp(rect.left + 1,rect.top + 1))|| el.contains(efp(rect.right-1,rect.top + 1))|| el.contains(efp(rect.right-1,rect.bottom-1))|| el.contains(efp(rect.left + 1,rect.bottom-1)) (2认同)

Ism*_*ooq 108

我们现在有一个原生的 javascript Intersection Observer API ,我们可以从中检测元素是否在视口中。

这是例子

const el = document.querySelector('#el')
const observer = new window.IntersectionObserver(([entry]) => {
  if (entry.isIntersecting) {
    console.log('ENTER')
    return
  }
  console.log('LEAVE')
}, {
  root: null,
  threshold: 0.1, // set offset 0.1 means trigger if atleast 10% of element in viewport
})

observer.observe(el);
Run Code Online (Sandbox Code Playgroud)
body {
  height: 300vh;
}

#el {
  margin-top: 100vh;
}
Run Code Online (Sandbox Code Playgroud)
<div id="el">this is element</div>
Run Code Online (Sandbox Code Playgroud)

  • 应选择此作为新的正确答案。 (21认同)

Wal*_*alf 59

我试过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() */;
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*ger 32

作为公共服务:
Dan的回答是正确的计算(元素可以是>窗口,特别是在手机屏幕上),正确的jQuery测试,以及添加isElementPartiallyInViewport:

顺便说一句,window.innerWidth和document.documentElement.clientWidth之间的区别在于clientWidth/clientHeight不包含滚动条,而window.innerWidth/Height则不包括滚动条.

function isElementPartiallyInViewport(el)
{
    //special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

    var rect = el.getBoundingClientRect();
    // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
    var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
    var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

    // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
    var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
    var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

    return (vertInView && horInView);
}


// http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
function isElementInViewport (el) 
{
    //special bonus for those using jQuery
    if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

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

    return (
           (rect.left >= 0)
        && (rect.top >= 0)
        && ((rect.left + rect.width) <= windowWidth)
        && ((rect.top + rect.height) <= windowHeight)
    );

}


function fnIsVis(ele)
{
    var inVpFull = isElementInViewport(ele);
    var inVpPartial = isElementPartiallyInViewport(ele);
    console.clear();
    console.log("Fully in viewport: " + inVpFull);
    console.log("Partially in viewport: " + inVpPartial);
}
Run Code Online (Sandbox Code Playgroud)

测试用例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
    <title>Test</title>
    <!--
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>    
    <script src="scrollMonitor.js"></script>
    -->

    <script type="text/javascript">

        function isElementPartiallyInViewport(el)
        {
            //special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];

            var rect = el.getBoundingClientRect();
            // DOMRect { x: 8, y: 8, width: 100, height: 100, top: 8, right: 108, bottom: 108, left: 8 }
            var windowHeight = (window.innerHeight || document.documentElement.clientHeight);
            var windowWidth = (window.innerWidth || document.documentElement.clientWidth);

            // http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap
            var vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
            var horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

            return (vertInView && horInView);
        }


        // http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
        function isElementInViewport (el) 
        {
            //special bonus for those using jQuery
            if (typeof jQuery !== 'undefined' && el instanceof jQuery) el = el[0];


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

            return (
                   (rect.left >= 0)
                && (rect.top >= 0)
                && ((rect.left + rect.width) <= windowWidth)
                && ((rect.top + rect.height) <= windowHeight)
            );

        }


        function fnIsVis(ele)
        {
            var inVpFull = isElementInViewport(ele);
            var inVpPartial = isElementPartiallyInViewport(ele);
            console.clear();
            console.log("Fully in viewport: " + inVpFull);
            console.log("Partially in viewport: " + inVpPartial);
        }


        // var scrollLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft,
        // var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

    </script>

</head>
<body>

    <div style="display: block; width: 2000px; height: 10000px; background-color: green;">

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <div style="background-color: crimson; display: inline-block; width: 800px; height: 500px;" ></div>
        <div id="myele" onclick="fnIsVis(this);" style="display: inline-block; width: 100px; height: 100px; background-color: hotpink;">
        t
        </div>

        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />
        <br /><br /><br /><br /><br /><br />

        <input type="button" onclick="fnIsVis(document.getElementById('myele'));" value="det" />

    </div>

    <!--
    <script type="text/javascript">

        var element = document.getElementById("myele");
        var watcher = scrollMonitor.create( element );

        watcher.lock();

        watcher.stateChange(function() {
            console.log("state changed");
            // $(element).toggleClass('fixed', this.isAboveViewport)
        });

    </script>
    -->
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

  • `isElementPartiallyInViewport`也非常有用.好一个. (2认同)
  • @Arun chauhan:我的代码都没有加载图像,所以为什么要加载图像,而且公式是正确的。 (2认同)

rya*_*nve 26

查看verge的源代码,它使用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 
    );

}
Run Code Online (Sandbox Code Playgroud)

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


小智 25

有一个名为inview的 jQuery插件可以完成这项工作

  • 我的团队遇到了一些关于这个插件的严重性能问题所以我去了一个分叉,改变了实现以使用getBoundingClientRect().请在此处查看:https://github.com/mmmeff/jquery.inview2 (16认同)

Eri*_*hen 25

我更短更快的版本.

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

根据需要添加jsFiddle https://jsfiddle.net/on1g619L/1/

  • 我的解决方案更贪婪和更快,当元素在视口中有任何像素时,它将返回false. (4认同)

r3w*_*3wt 21

我发现没有jQuery可用的功能的中心版本令人不安.当我遇到Dan的解决方案时,我发现有机会为那些喜欢以jQueryOO风格编程的人提供一些东西.请务必向上滚动并在Dan的代码上保留一个upvote.它漂亮而活泼,对我来说就像是一种魅力.

bada bing bada boom

$.fn.inView = function(){
    if(!this.length) return false;
    var rect = this.get(0).getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );

};

//additional examples for other use cases
//true false whether an array of elements are all in view
$.fn.allInView = function(){
    var all = [];
    this.forEach(function(){
        all.push( $(this).inView() );
    });
    return all.indexOf(false) === -1;
};

//only the class elements in view
$('.some-class').filter(function(){
    return $(this).inView();
});

//only the class elements not in view
$('.some-class').filter(function(){
    return !$(this).inView();
});
Run Code Online (Sandbox Code Playgroud)

用法

$(window).on('scroll',function(){ 

    if( $('footer').inView() ) {
        // do cool stuff
    }

});
Run Code Online (Sandbox Code Playgroud)


leo*_*ess 14

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

function isInView(el) {
  const box = el.getBoundingClientRect();
  return box.top < window.innerHeight && box.bottom >= 0;
}
Run Code Online (Sandbox Code Playgroud)


Ada*_*hal 8

我发现这里接受的答案对于大多数用例而言过于复杂.此代码可以很好地完成工作(使用JQuery),并区分完全可见和部分可见的元素.

var element         = $("#element");
var topOfElement    = element.offset().top;
var bottomOfElement = element.offset().top + element.outerHeight(true);
var $window         = $(window);

$window.bind('scroll', function() {

    var scrollTopPosition   = $window.scrollTop()+$window.height();
    var windowScrollTop     = $window.scrollTop()

    if( windowScrollTop > topOfElement && windowScrollTop < bottomOfElement) {
       // Element is partially visible (above viewable area)
       console.log("Element is partially visible (above viewable area)");

    }else if( windowScrollTop > bottomOfElement && windowScrollTop > topOfElement ) {
        // Element is hidden (above viewable area)
       console.log("Element is hidden (above viewable area)");

    }else if( scrollTopPosition < topOfElement && scrollTopPosition < bottomOfElement ) {
        // Element is hidden (below viewable area)
        console.log("Element is hidden (below viewable area)");

    }else if( scrollTopPosition < bottomOfElement && scrollTopPosition > topOfElement ) {
        // Element is partially visible (below viewable area)
        console.log("Element is partially visible (below viewable area)");

    }else{
        // Element is completely visible
        console.log("Element is completely visible");
    }
});
Run Code Online (Sandbox Code Playgroud)


Art*_*ain 8

视口信息

\n
/**\n * Returns Element placement information in Viewport\n * @link https://stackoverflow.com/a/70476497/2453148\n *\n * @typedef {object} ViewportInfo - Whether the element is\xe2\x80\xa6\n * @property {boolean} isInViewport - fully or partially in the viewport\n * @property {boolean} isPartiallyInViewport - partially in the viewport\n * @property {boolean} isInsideViewport - fully inside viewport\n * @property {boolean} isAroundViewport - completely covers the viewport\n * @property {boolean} isOnEdge - intersects the edge of viewport\n * @property {boolean} isOnTopEdge - intersects the top edge\n * @property {boolean} isOnRightEdge - intersects the right edge\n * @property {boolean} isOnBottomEdge - is intersects the bottom edge\n * @property {boolean} isOnLeftEdge - is intersects the left edge\n *\n * @param el Element\n * @return {Object} ViewportInfo\n */\nfunction getElementViewportInfo(el) {\n\n    let result = {};\n\n    let rect = el.getBoundingClientRect();\n    let windowHeight = window.innerHeight || document.documentElement.clientHeight;\n    let windowWidth  = window.innerWidth || document.documentElement.clientWidth;\n\n    let insideX = rect.left >= 0 && rect.left + rect.width <= windowWidth;\n    let insideY = rect.top >= 0 && rect.top + rect.height <= windowHeight;\n\n    result.isInsideViewport = insideX && insideY;\n\n    let aroundX = rect.left < 0 && rect.left + rect.width > windowWidth;\n    let aroundY = rect.top < 0 && rect.top + rect.height > windowHeight;\n\n    result.isAroundViewport = aroundX && aroundY;\n\n    let onTop    = rect.top < 0 && rect.top + rect.height > 0;\n    let onRight  = rect.left < windowWidth && rect.left + rect.width > windowWidth;\n    let onLeft   = rect.left < 0 && rect.left + rect.width > 0;\n    let onBottom = rect.top < windowHeight && rect.top + rect.height > windowHeight;\n\n    let onY = insideY || aroundY || onTop || onBottom;\n    let onX = insideX || aroundX || onLeft || onRight;\n\n    result.isOnTopEdge    = onTop && onX;\n    result.isOnRightEdge  = onRight && onY;\n    result.isOnBottomEdge = onBottom && onX;\n    result.isOnLeftEdge   = onLeft && onY;\n\n    result.isOnEdge = result.isOnLeftEdge || result.isOnRightEdge ||\n        result.isOnTopEdge || result.isOnBottomEdge;\n\n    let isInX =\n        insideX || aroundX || result.isOnLeftEdge || result.isOnRightEdge;\n    let isInY =\n        insideY || aroundY || result.isOnTopEdge || result.isOnBottomEdge;\n\n    result.isInViewport = isInX && isInY;\n\n    result.isPartiallyInViewport =\n        result.isInViewport && result.isOnEdge;\n\n    return result;\n}\n
Run Code Online (Sandbox Code Playgroud)\n


All*_*lly 6

这是我的解决方案。如果元素隐藏在可滚动容器内,它将起作用。

这是一个演示(尝试调整窗口大小)

var visibleY = function(el){
    var top = el.getBoundingClientRect().top, rect, el = el.parentNode;
    do {
        rect = el.getBoundingClientRect();
        if (top <= rect.bottom === false)
            return false;
        el = el.parentNode;
    } while (el != document.body);
    // Check it's within the document viewport
    return top <= document.documentElement.clientHeight;
};
Run Code Online (Sandbox Code Playgroud)

我只需要检查它在 Y 轴上是否可见(对于滚动 Ajax 加载更多记录功能)。


Dom*_*see 6

我在这里遇到的所有答案只检查元素是否位于当前视口内.但这并不意味着它是可见的.
如果给定元素位于具有溢出内容的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;
};
Run Code Online (Sandbox Code Playgroud)

该解决方案忽略了由于其他事实而导致元素可能不可见的事实,例如opacity: 0.

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


ton*_*ton 5

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

当您的元素在其他可滚动 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)
    );
};
Run Code Online (Sandbox Code Playgroud)


Ran*_*urn 5

新的Intersection Observer API非常直接地解决了这个问题。

此解决方案将需要一个polyfill,因为Safari,Opera和IE尚不支持此功能。(溶液中包含了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'));
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)

  • 良好的实现,并根据[此答案](/sf/answers/3642485591/)中的链接,通过在HTML中添加`&lt;!DOCTYPE html&gt; (2认同)
  • @KarthikChintala - 除了 IE 之外的所有浏览器都支持它 - 还有一个 polyfill 可用。 (2认同)

Dak*_*san 5

在 Android 上放大 Google 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);
Run Code Online (Sandbox Code Playgroud)