Как определить, виден ли элемент DOM в текущем окне просмотра?

StackOverflow https://stackoverflow.com/questions/123999

Вопрос

Есть ли эффективный способ определить, виден ли элемент DOM (в HTML-документе) в данный момент (отображается в окно просмотра)?

(Вопрос относится к Firefox)

Это было полезно?

Решение

Обновить: Время идет, как и наши браузеры. Этот метод больше не рекомендуется и вы должны использовать решение @Dan, приведенное ниже (https://stackoverflow.com/a/7557433/5628) если вам не нужна поддержка IE<7.

Оригинальное решение (в настоящее время устаревшее):

Это проверит, полностью ли элемент виден в текущем окне просмотра:

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

Другие советы

Сейчас большинство браузеров Поддержка 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() */
    );
}

Как использовать:

Вы можете быть уверены, что приведенная выше функция возвращает правильный ответ в момент времени, когда она вызывается, но как насчет отслеживания видимости элемента как события?

Разместите следующий код в нижней части вашего <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);
}
*/

Если вы внесете какие-либо изменения в DOM, они, конечно, могут изменить видимость вашего элемента.

Рекомендации и распространенные подводные камни:

Может быть, вам нужно отслеживать масштабирование страницы / ущипывание мобильного устройства? jQuery должен обрабатывать масштабирование/пинч кроссбраузерный, в противном случае Первый или второй ссылка должна вам помочь.

Если вы изменить DOM, это может повлиять на видимость элемента.Вы должны взять это под контроль и позвонить handler() вручную.К сожалению, у нас нет кроссбраузерности onrepaint событие.С другой стороны, это позволяет нам проводить оптимизацию и выполнять повторную проверку только для модификаций DOM, которые могут изменить видимость элемента.

Никогда, никогда используйте это внутри jQuery $(документ).готово () только потому, что нет никакой гарантии, что CSS был применен в этот момент.Ваш код может работать локально с вашим CSS на жестком диске, но после установки на удаленный сервер он выйдет из строя.

После DOMContentLoaded уволен, применяемые стили, но изображения еще не загружены.Итак, мы должны добавить window.onload прослушиватель событий.

Мы пока не можем поймать событие масштабирования / ущипывания.

Последним средством может быть следующий код:

/* TODO: this looks like a very bad code */
setInterval(handler, 600); 

Вы можете использовать потрясающую функцию Просмотр страницы HTML5 API, если вас волнует, активна ли и видна ли вкладка с вашей веб-страницей.

TODO:этот метод не обрабатывает две ситуации:

Обновить

В современных браузерах вы, возможно, захотите ознакомиться с API-интерфейс Наблюдателя пересечения который обеспечивает следующие преимущества:

  • Лучшая производительность по сравнению с прослушиванием событий прокрутки
  • Работает в междоменных iframes
  • Может определить, загораживает ли элемент / пересекает ли другой

Intersection Observer находится на пути к тому, чтобы стать полноценным стандартом, и уже поддерживается в Chrome 51 +, Edge 15 + и Firefox 55 +, а также находится в стадии разработки для Safari.Там также есть полифилл Доступно.


Предыдущий ответ

Есть некоторые проблемы с ответ предоставлен Дэном это может сделать его неподходящим подходом для некоторых ситуаций.Некоторые из этих проблем указаны в его ответе внизу, что его код будет выдавать ложные срабатывания для элементов, которые:

  • Скрытый другим элементом перед тестируемым
  • За пределами видимой области родительского или предкового элемента
  • Элемент или его дочерние элементы, скрытые с помощью CSS clip собственность

Эти ограничения продемонстрированы в следующих результатах простой тест:

Failed test, using 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/

И вот результат:

Passed test, using isElementVisible

Дополнительные примечания

Однако этот метод не лишен своих собственных ограничений.Например, тестируемый элемент с меньшим z-индексом, чем у другого элемента в том же местоположении, будет идентифицирован как скрытый, даже если элемент впереди фактически не скрывает никакой его части.Тем не менее, этот метод имеет свое применение в некоторых случаях, которые решение Дэна не охватывает.

И то , и другое element.getBoundingClientRect() и document.elementFromPoint() являются частью рабочего проекта спецификации CSSOM и поддерживаются как минимум в IE 6 и более поздних версиях, а также большинство настольные браузеры работают уже давно (хотя и не идеально).Видишь Quirksmode для этих функций для получения дополнительной информации.

contains() используется для проверки того, возвращен ли элемент document.elementFromPoint() является дочерним узлом элемента, который мы тестируем на видимость.Он также возвращает true, если возвращаемый элемент является тем же самым элементом.Это просто делает проверку более надежной.Он поддерживается во всех основных браузерах, Firefox 9.0 является последним из них, добавившим его.Для получения более старой поддержки Firefox проверьте историю этого ответа.

Если вы хотите проверить больше точек вокруг элемента на видимость ― т. е. убедиться, что элемент не покрыт более чем, скажем, на 50% ― не потребуется много времени, чтобы скорректировать последнюю часть ответа.Однако имейте в виду, что это, вероятно, было бы очень медленно, если бы вы проверяли каждый пиксель, чтобы убедиться, что он виден на 100%.

Я пытался Ответ Дэна однако алгебра, используемая для определения границ, означает, что элемент должен быть как меньше размера области просмотра, так и полностью находиться внутри области просмотра, чтобы получить true, что легко приводит к ложноотрицательным результатам.Если вы хотите определить, находится ли элемент вообще в окне просмотра, ответ райанве находится близко, но тестируемый элемент должен перекрывать область просмотра, поэтому попробуйте следующее:

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

Как государственная служба:
Ответ Дэна с правильными вычислениями (элемент может быть> window, особенно на экранах мобильных телефонов) и правильным тестированием 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);
}

Тестовый пример

<!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>

Существует плагин jQuery, который называется просмотр это делает свое дело

Смотрите источник грань, который использует 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 если Любой часть элемента находится в окне просмотра.

моя более короткая и быстрая версия.

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/

Меня беспокоило, что не было jQuery доступна ориентированная на пользователя версия функциональности.Когда я наткнулся на Dan's solution я подсмотрел возможность предоставить что-нибудь для людей, которые любят программировать на jQuery Стиль ОО.Обязательно прокрутите страницу вверх и оставьте положительный отзыв о коде Дэна.Это приятно и быстро, и для меня работает как заклинание.

бада-бинг-бада- бум

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

использование

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

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

});

Я нахожу, что принятый здесь ответ слишком сложен для большинства случаев использования.Этот код хорошо выполняет свою работу (используя 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");
    }
});

Все ответы, с которыми я столкнулся здесь, проверяют только, является ли элемент расположен внутри текущего окна просмотра.Но это это не значит, что это видно.
Что делать, если данный элемент находится внутри 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.

Новый API-интерфейс Наблюдателя пересечения обращается к этому вопросу очень прямо.

Для этого решения потребуется полизаполнение, так как Safari, Opera и IE это пока не поддерживают.(полифилл входит в состав раствора).

В этом решении вне поля зрения находится прямоугольник, который является целью (наблюдаемым).Когда он появляется в поле зрения, кнопка вверху заголовка скрывается.Это отображается, как только поле покидает вид.

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>

Я думаю, что это более функциональный способ сделать это.Ответ Дэна не работает в рекурсивном контексте.

Эта функция решает проблему, когда ваш элемент находится внутри других прокручиваемых разделов, рекурсивно проверяя любые уровни, расположенные выше 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)
    );
};

Основываясь на решении @dan, приведенном выше (https://stackoverflow.com/a/7557433/5628), я попытался очистить реализацию, чтобы было проще использовать ее несколько раз на одной странице:

$(function() {

  $(window).on('load resize scroll', function() {
    addClassToElementInViewport($('.bug-icon'), 'animate-bug-icon');
    addClassToElementInViewport($('.another-thing'), 'animate-thing');
    // 👏 repeat as needed ...
  });

  function addClassToElementInViewport(element, newClass) {
    if (inViewport(element)) {
      element.addClass(newClass);
    }
  }

  function inViewport(element) {
    if (typeof jQuery === "function" && element instanceof jQuery) {
      element = element[0];
    }
    var elementBounds = element.getBoundingClientRect();
    return (
      elementBounds.top >= 0 &&
      elementBounds.left >= 0 &&
      elementBounds.bottom <= $(window).height() &&
      elementBounds.right <= $(window).width()
    );
  }

});

Способ, которым я его использую, заключается в том, что когда элемент прокручивается в поле зрения, я добавляю класс, который запускает анимацию ключевого кадра css.Это довольно просто и работает особенно хорошо, когда у вас есть более 10 объектов для условной анимации на странице.

Надеюсь, это поможет!

Вот мое решение, оно будет работать, если элемент скрыт внутри контейнера с возможностью прокрутки.

Вот демо-версия (попробуйте изменить размер окна, чтобы)

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 its within the document viewport
    return top <= document.documentElement.clientHeight;
};

Мне нужно было только проверить, видно ли это по оси Y (для функции прокрутки ajax загружает больше записей).

Простое и компактное решение, которое сработало для меня.

Пример Вы хотите посмотреть, виден ли элемент в родительском элементе, у которого есть прокрутка переполнения.

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

     var container = $('#sidebar');
     var containerHeight = container.height();
     var scrollPosition = $('#row1').offset().top - container.offset().top;

     if (containerHeight < scrollPosition) {
         console.log('not visible');
     } else {
         console.log('visible');
     }
})

Лучшее решение:

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

Проверяет, находится ли элемент хотя бы частично в поле зрения (вертикальный размер):

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

У меня был тот же вопрос, и я решил его с помощью getBoundingClientRect().Этот код полностью "универсальный" и должен быть написан только один раз, чтобы он заработал (вам не нужно записывать его для каждого элемента, который, как вы хотите знать, находится в окне просмотра).Этот код проверяет только, находится ли он вертикально в окне просмотра не горизонтально.В этом случае переменная (массив) 'elements' содержит все элементы, которые вы проверяете на вертикальное расположение в окне просмотра, поэтому возьмите любые элементы, которые вы хотите, в любом месте и сохраните их там."Цикл for" перебирает каждый элемент и проверяет, находится ли он вертикально в окне просмотра.Этот код выполняет каждый раз, когда пользователь прокручивает!Если getBoudingClientRect().top занимает менее 3/4 области просмотра (элемент занимает одну четверть области просмотра), он регистрируется как "в области просмотра".Поскольку код является универсальным, вы захотите знать, "какой" элемент находится в окне просмотра.Чтобы выяснить это, вы можете определить его по пользовательскому атрибуту, имени узла, идентификатору, имени класса и многому другому.Вот мой код (скажите мне, если он не работает, он был протестирован в IE 11, FireFox 40.0.3, Chrome версии 45.0.2454.85 m, Opera 31.0.1889.174 и Edge с Windows 10, [пока нет Safari])...

//scrolling handlers...
window.onscroll = function(){
  var elements = document.getElementById('whatever').getElementsByClassName('whatever');
  for(var i = 0; i != elements.length; i++)
  {
   if(elements[i].getBoundingClientRect().top <= window.innerHeight*0.75 && elements[i].getBoundingClientRect().top > 0)
   {
      console.log(elements[i].nodeName + ' ' + elements[i].className + ' ' + elements[i].id + ' is in the viewport; proceed with whatever code you want to do here.');
   }
};

Надеюсь, это кому-то поможет :-)

Вот функция, которая сообщает, является ли элемент видимым в текущем окне просмотра родитель элемент:

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

Настолько проще, насколько это возможно, IMO:

function isVisible(elem) {
  var coords = elem.getBoundingClientRect();
  return Math.abs(coords.top) <= coords.height;
}

Я использую эту функцию (она только проверяет, находится ли y на экране, поскольку в большинстве случаев x не нужен)

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

}

Мне действительно понравилось участвовать в подобном испытании в этом суть который предоставляет полинаполнитель для scrollIntoViewIfNeeded().

Все необходимые вопросы по кунг-фу, необходимые для ответа, находятся в этом блоке:

var parent = this.parentNode,
    parentComputedStyle = window.getComputedStyle(parent, null),
    parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
    parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
    overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
    overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
    overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
    overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
    alignWithTop = overTop && !overBottom;

this ссылается на элемент, который вы хотите знать, является ли он, т.е. overTop или overBottom - просто должен уловить суть дела...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top