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

787

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

(Вопрос касается Firefox)

  • 0
    Зависит от того, что вы подразумеваете под видимым. Если вы имеете в виду, отображается ли он в данный момент на странице, учитывая положение прокрутки, вы можете рассчитать его на основе смещения элементов y и текущей позиции прокрутки.
  • 15
    Пояснение: видимое, как внутри текущего отображаемого прямоугольника. Видимый, как в не скрытом или отображаемом виде: нет? Видно, как не за чем-то еще в Z-порядке?
Показать ещё 6 комментариев
Теги:
dom
firefox

25 ответов

301
Лучший ответ

Обновление: Время идет, и у нас есть наши браузеры. Этот метод больше не рекомендуется, и вы должны использовать решение @Dan ниже (https://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport), если вам не нужно поддерживать 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
  );
}
  • 1
    В оригинальной опубликованной функции произошла ошибка. Необходимо сохранить ширину / высоту перед переназначением эл ...
  • 0
    Также было бы разумно немного абстрагироваться и создать некоторые служебные функции. Я использую один с именем getViewport, который возвращает верхний / левый / нижний / правый угол видимого окна, и один с именем getPosition, который находит верхний / левый угол элемента.
Показать ещё 9 комментариев
1279

Теперь большинство браузеров поддерживает 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: этот метод не обрабатывает две ситуации:

  • 5
    Я использую это решение (остерегайтесь опечатки "botom", хотя). Есть также кое-что, о чем нужно знать, когда рассматриваемый нами элемент будет содержать изображения. Chrome (как минимум) должен ждать загрузки изображения, чтобы иметь точное значение boundingRectangle. Кажется, что у Firefox нет этой "проблемы"
  • 1
    window.innerHeight и innerWidth не определены в IE. На сайте howtocreate.co.uk/tutorials/javascript/browserwindow есть хорошая статья, в которой показаны различные способы определения высоты документа ... document.body.clientHeight, похоже, лучший способ в целом. Я отредактировал ваш ответ соответственно.
Показать ещё 33 комментария
132

Update

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

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

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


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

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

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

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

Изображение 2218

Решение: 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/

И результат:

Изображение 2219

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

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

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

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

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

  • 1
    Вы хотели использовать doc.documentElement.clientWidth? Должно ли это быть «document.documentElement» вместо этого? С другой стороны, это единственный метод, который также работает для вариантов использования, таких как скрытие содержимого элемента для доступности с помощью свойства CSS «clip»: snook.ca/archives/html_and_css/hiding-content-for-accessibility
  • 0
    @klamping: хороший улов, спасибо. Я скопировал его прямо из своего кода, где я использовал doc в качестве псевдонима для document . Да, мне нравится думать об этом как о приличном решении для крайних случаев.
Показать ещё 18 комментариев
46

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

Как общественная служба:
Дэн отвечает правильными вычислениями (элемент может быть > окном, особенно на экранах мобильного телефона), и исправить тестирование 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>
  • 2
    isElementPartiallyInViewport очень полезен. Хороший.
  • 0
    Для isElementInViewport (e): Ваш код даже не загружает изображения. Это правильно: function isElementInViewport (e) {var t = e.getBoundingClientRect (); return t.top> = 0 && t.left> = 0 && t.bottom <= (window.innerHeight || document.documentElement.clientHeight) && t.right <= (window.innerWidth || document.documentElement.clientWidth) }
Показать ещё 5 комментариев
24

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

  • 16
    Моя команда столкнулась с некоторыми серьезными проблемами с производительностью с этим плагином, поэтому я пошел наперегонки и поменял его, изменив реализацию для использования getBoundingClientRect (). Проверьте это здесь: github.com/mmmeff/jquery.inview2
  • 0
    Другое решение
23

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

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/

  • 4
    Мое решение более жадное и более быстрое, когда элемент имеет какой-либо пиксель в области просмотра, он возвращает false.
  • 1
    Мне это нравится. Сжатый. Вы можете удалить пробелы между именем функции и круглыми скобками, а также между круглыми скобками и фигурными скобками в первой строке. Никогда не любил эти места. Может быть, это всего лишь мой текстовый редактор, который все это кодирует цветом, который все еще облегчает чтение. функция aaa (arg) {операторы} Я знаю, что это не заставляет это выполняться быстрее, вместо этого подпадает под минимизацию.
Показать ещё 1 комментарий
23

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

19

Мне показалось, что проблема в том, что доступной функциональности не существует 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
    }

});
  • 1
    Будет ли фигурная скобка достаточной для закрытия оператора if?
  • 1
    Я не мог заставить его работать с несколькими элементами одного и того же класса.
Показать ещё 1 комментарий
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");
    }
});
  • 0
    Работает блестяще! Протестировано на Chrome, Firefox и Safari.
  • 0
    Вы можете использовать это .
Показать ещё 1 комментарий
7

http://www.appelsiini.net/projects/viewport

Отличный простой в использовании плагин, просто используйте: in-viewport

3

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

3

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

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

Основываясь на вышеприведенном решении @dan (https://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport), я решил выполнить очистку, так что использовать его несколько раз на одной странице проще:

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

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

  • 0
    Вы обязательно должны кэшировать $window = $(window) вне обработчика прокрутки
2

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

1

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

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

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

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

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

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

0

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

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

У меня был тот же вопрос, и я понял, используя 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.');
   }
};

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

0

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

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

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

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;    
}
  • 10
    Вы должны попытаться объяснить, почему ваша версия лучше. В нынешнем виде он выглядит примерно так же, как и другие решения.
  • 0
    Отличное решение, оно имеет BOX / ScrollContainer и не использует WINDOW (только если оно не указано). Взгляните на код, чем оцените, это более универсальное решение (много искал)
-1

Мне нужно решение 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);
});
-2

Это мой один лайнер (требуется 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'))

-2

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

}
-2

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню