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

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

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

Ответы

Ответ 1

Обновление: Время идет, и у нас есть наши браузеры. Этот метод больше не рекомендуется, и вы должны использовать решение @Dan ниже (fooobar.com/questions/10631/...), если вам не нужно поддерживать 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
  );
}

Ответ 2

Теперь большинство браузеров поддерживает 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 $(document).ready(), потому что в данный момент не существует гарантии CSS. Ваш код может работать локально с вашим CSS на жестком диске, но после его установки на удаленном сервере он не работает.

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

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

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

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

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

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

Ответ 3

Update

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

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

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


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

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

  • Скрывается другим элементом перед тестируемым
  • Вне видимой области элемента parent или ancestor
  • Элемент или его дочерние элементы скрыты с помощью свойства 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 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-индексом, чем другой элемент в том же месте, будет идентифицирован как скрытый, даже если элемент впереди фактически не скрывает какой-либо его части. Тем не менее, этот метод использует его в некоторых случаях, когда решение Dan не распространяется.

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

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

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

Ответ 4

Я попробовал Дэн ответить, однако алгебра, используемая для определения границ, означает, что элемент должен быть как ≤ размером окна просмотра, так и полностью внутри области просмотра, чтобы получить 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() */;
}

Ответ 5

Как общественная служба:
Дэн отвечает правильными вычислениями (элемент может быть > окном, особенно на экранах мобильного телефона), и исправить тестирование jQuery, а также добавить isElementPartiallyInViewport:

Кстати, разница между window.innerWidth и document.documentElement.clientWidth заключается в том, что clientWidth/clientHeight не включает полосу прокрутки, а window.innerWidth/Высота делает.

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>

Ответ 6

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

Ответ 7

См. источник 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 
    );

}

Возвращает true, если любая часть элемента находится в окне просмотра.

Ответ 8

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

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/

Ответ 9

Мне показалось, что проблема в том, что доступной функциональности не существует jQuery. Когда я наткнулся на решение Dan, я попробовал предоставить что-то для людей, которые любят программировать в стиле jQuery OO. Обязательно прокрутите страницу вверх и оставите надпись на коду Дэн. Его приятно и быстро и работает как прелесть для меня.

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

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

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

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

});

Ответ 10

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

Ответ 12

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

Ответ 13

Новый API-интерфейс Intersection Observer решает этот вопрос очень прямо.

Для этого решения потребуется полифилл, поскольку 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>

Ответ 14

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

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

Ответ 15

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

Ответ 16

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

Здесь демо (попробуйте изменить размер окна)

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 load больше функций записи).

Ответ 17

Основываясь на вышеприведенном решении @dan (fooobar.com/questions/10631/...), я решил выполнить очистку, так что использовать его несколько раз на одной странице проще:

$(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+ вещей, которые можно условно охарактеризовать на странице.

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

Ответ 18

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

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

Ответ 19

Легкое и маленькое решение, которое сработало для меня.

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

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

Ответ 20

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

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

Ответ 21

У меня был тот же вопрос, и я понял, используя getBoundingClientRect(). Этот код полностью "общий" и должен быть написан только один раз для его работы (вам не нужно записывать его для каждого элемента, который вы хотите знать, в окне просмотра). Этот код проверяет только, находится ли он вертикально в окне просмотра не горизонтально. В этом случае переменные (массивы) "элементы" содержат все элементы, которые вы проверяете, чтобы они были вертикально в окне просмотра, поэтому хватайте любые элементы, которые вы хотите где угодно, и храните их там. Цикл "for" циклически проходит через каждый элемент и проверяет, находится ли он вертикально в окне просмотра. Этот код выполняет каждый раз, который прокручивает пользователь! Если верхняя часть getBoudingClientRect() меньше 3/4, то окно просмотра (элемент составляет одну четверть в окне просмотра), он регистрируется как "в окне просмотра". Поскольку код является общим, вам нужно знать, какой элемент находится в окне просмотра. Чтобы узнать это, вы можете определить его по пользовательскому атрибуту, node имя, идентификатор, имя класса и т.д. Вот мой код (скажите, если он не работает, он был протестирован в 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.');
   }
};

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

Ответ 22

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

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

Ответ 23

Как можно проще получить IMO:

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

Ответ 24

Самое простое решение, поскольку совместимость с Element.getBoundingClientRect() стала идеальной:

function inView(el) {
    let box = el.getBoundingClientRect();
    return box.top < window.innerHeight && box.bottom >= 0;
}

Ответ 25

Внимание всем!

В большинстве случаев здесь происходит сбой в следующих точках:

-When любой пиксель элемента виден, но не "angular",

Элемент -When больше, чем область просмотра, и по центру,

-Most из них проверяют только отдельный элемент внутри документа или окна.

Для всех этих проблем у меня есть решение и плюсы:

-You может возвращать visible, когда появляется только пиксель с любой стороны и не является углом,

-You все еще может возвращать visible, в то время как элемент больше, чем область просмотра,

-You может выбрать свой parent element или вы можете автоматически позволить ему выбрать,

-Works on dynamically added elements too.

Если вы посмотрите приведенные ниже фрагменты, вы увидите, что разница в использовании overflow-scroll в контейнере элементов не вызовет никаких проблем, и вы увидите, что НЕ ДРУГИЕ ОТВЕТЫ ЗДЕСЬ, даже если пиксель отображается с ЛЮБОЙ СТОРОНЫ или когда элемент больше, чем область просмотра, и мы видим, что ВНУТРЕННИХ ПИКСЕЛЕЙ ЭЛЕМЕНТА он все еще работает.

Использование простое:

// For checking element visibility from any sides
isVisible(element)

// For checking elements visibility in a parent you would like to check
var parent = document; // Asuming you check if element inside document
isVisible(element, parent)

// For checking elements visibility even if its bigger then viewport
isVisible(element, null, true) // Without parent choise
isVisible(element, parent, true) // With parent choise

Демонстрация без crossSearchAlgorithm, которая полезна для элементов, которые больше, чем область просмотра, проверяют внутренние пиксели element3, чтобы увидеть:

function isVisible(element, parent, crossSearchAlgorithm) {
    var rect = element.getBoundingClientRect(),
    		prect = (parent != undefined) ? parent.getBoundingClientRect() : element.parentNode.getBoundingClientRect(),
        csa = (crossSearchAlgorithm != undefined) ? crossSearchAlgorithm : false,
        efp = function (x, y) { return document.elementFromPoint(x, y) };
    // Return false if it not in the viewport
    if (rect.right < prect.left || rect.bottom < prect.top || rect.left > prect.right || rect.top > prect.bottom) {
        return false;
    }
    var flag = false;
    // Return true if left to right any border pixel reached 
    for (var x = rect.left; x < rect.right; x++) {
    	if (element.contains(efp(rect.top, x)) || element.contains(efp(rect.bottom, x))) {
        flag = true;
        break;
      }
    }
    // Return true if top to bottom any border pixel reached 
    if (flag == false) {
      for (var y = rect.top; y < rect.bottom; y++) {
        if (element.contains(efp(rect.left, y)) || element.contains(efp(rect.right, y))) {
          flag = true;
          break;
        }
      }
    }
    if(csa) {
      // Another algorithm to check if element is centered and bigger than viewport
      if (flag == false) {
        var x = rect.left;
        var y = rect.top;
        // From top left to bottom right
        while(x < rect.right || y < rect.bottom) {
          if (element.contains(efp(x,y))) {
            flag = true;
            break;
          }
          if(x < rect.right) { x++; }
          if(y < rect.bottom) { y++; }
        }
        if (flag == false) {
          x = rect.right;
          y = rect.top;
          // From top right to bottom left
          while(x > rect.left || y < rect.bottom) {
            if (element.contains(efp(x,y))) {
              flag = true;
              break;
            }
            if(x > rect.left) { x--; }
            if(y < rect.bottom) { y++; }
          }
        }
      }
    }
    return flag;
}

// Check multiple elements visibility
document.getElementById('container').addEventListener("scroll", function() {
	var elementList = document.getElementsByClassName("element");
  var console = document.getElementById('console');
	for (var i=0; i < elementList.length; i++) {
  	// I did not define parent so it will be element parent
    if (isVisible(elementList[i])) {
  		console.innerHTML = "Element with id[" + elementList[i].id + "] is visible!";
      break;
    } else {
    	console.innerHTML = "Element with id[" + elementList[i].id + "] is hidden!";
    }
  }
});

// Dynamically added elements
for(var i=4; i <= 6; i++) {
  var newElement = document.createElement("div");
  newElement.id = "element" + i;
  newElement.classList.add("element");
  document.getElementById('container').appendChild(newElement);
}
#console { background-color: yellow; }
#container {
  width: 300px;
  height: 100px;
  background-color: lightblue;
  overflow-y: auto;
  padding-top: 150px;
  margin: 45px;
}
.element {
  margin: 400px;
  width: 400px;
  height: 320px;
  background-color: green;
}
#element3 {
  position: relative;
  margin: 40px;
  width: 720px;
  height: 520px;
  background-color: green;
}
#element3::before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  margin: 0px;
  width: 740px;
  height: 540px;
  border: 5px dotted green;
  background: transparent;
}
<div id="console"></div>
<div id="container">
    <div id="element1" class="element"></div>
    <div id="element2" class="element"></div>
    <div id="element3" class="element"></div>
</div>

Ответ 26

Я использую эту функцию (она только проверяет, является ли 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;
    }

}

Ответ 27

Это мой один лайнер (требуется jQuery):

let isInView = (elem)=>(($(elem).offset().top + $(elem).height() - 800 <= $(window).scrollTop() + $(window).height()) && ($(elem).offset().top + 600 >= $(window).scrollTop()));

Функция возвращает true, если элемент находится в представлении, а false - если нет. Он выдает ошибку, если элемент не существует.

Вы можете вызвать функцию следующим образом: isInView($('#my-div'))

Ответ 28

Для подобной задачи мне действительно понравился этот смысл, который предоставляет polyfill для 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 относится к элементу, который вы хотите знать, если он есть i.e. overTop или overBottom - просто должен получить дрейф...

Ответ 29

Мне нужно решение jQuery, которое является простым (только для проверки вертикальной прокрутки), поэтому я изменил код Адама Рехалса. В этом примере щелкните все кнопки в окне просмотра, проверяя 5 раз в секунду. (кнопки становятся отключенными при нажатии на кнопку btw)

$(function () {
    var loadButtons = $('.btn-load-more');

    function clickButtonsIfInViewport() {
        loadButtons.each(function () {
            var element = $(this);
            var elementOffset = element.offset();
            var elementTop = elementOffset.top;
            var elementBottom = elementOffset.top + element.outerHeight(true);

            var windowTop = $window.scrollTop();
            var windowBottom = windowTop + $window.height();

            if (windowTop > elementBottom || windowBottom < elementTop) {
                return;
            }

            element.click();
        });
    }

    setInterval(clickButtonsIfInViewport, 200);
});