How to tell if a DOM element is visible in the cur

2018-12-30 23:42发布

Is there an efficient way to tell if a DOM element (in an HTML document) is currently visible (appears in the viewport)?

(The question regards Firefox)

22条回答
皆成旧梦
2楼-- · 2018-12-30 23:52

Depends what you mean by visible. If you mean is it currently shown on the page, given the scroll position, you can calculate it based on the elements y offset and the current scroll position.

查看更多
梦该遗忘
3楼-- · 2018-12-30 23:54

I found it troubling that there wasn't a jQuery centric version of the functionality available. When i came across Dan's solution i spied the opportunity to provide something for folks who like to program in the jQuery OO style. Be sure to scroll up and leave an upvote on Dan's code. Its nice and snappy and works like a charm for me.

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

usage

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

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

});
查看更多
萌妹纸的霸气范
4楼-- · 2018-12-30 23:55

Update: Time marches on and so have our browsers. This technique is no longer recommended and you should use @Dan's solution below (https://stackoverflow.com/a/7557433/5628) if you do not need to support IE<7.

Original solution (now outdated):

This will check if the element is entirely visible in the current viewport:

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

You could modify this simply to determine if any part of the element is visible in the viewport:

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
  );
}
查看更多
刘海飞了
5楼-- · 2018-12-30 23:55

I tried Dan's answer however the algebra used to determine the bounds means that the element must be both ≤ the viewport size and completely inside the viewport to get true, easily leading to false negatives. If you want to determine whether an element is in the viewport at all, ryanve's answer is close but the element being tested should overlap the viewport, so try this:

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() */;
}
查看更多
怪性笑人.
6楼-- · 2018-12-30 23:56

Based on @dan's solution above (https://stackoverflow.com/a/7557433/5628), I had a go at cleaning up implementation so that using it multiple times on the same page is easier:

$(function() {

  $(window).on('load resize scroll', function() {
    addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon');
    addClassToElementInViewport($('.another-thing'), 'animate-thing');
    //                                                                     
查看更多
笑指拈花
7楼-- · 2018-12-30 23:59

I think this is a more functional way to do it. The Dan's answer do not work in recursive context.

This function solve the problem when your element is inside others scrollable divs by testing any levels recursively upper to the HTML tag, and stops in the first 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)
    );
};
查看更多
登录 后发表回答