Jak sprawdzić, czy element DOM jest widoczny w bieżącym widoku?

Czy istnieje skuteczny sposób, aby stwierdzić, czy element DOM (w dokumencie HTML) jest obecnie widoczny (pojawia się w widoku )?

(pytanie dotyczy Firefoksa)

Author: Animesh, 2008-09-24

22 answers

Update: Czas maszeruje dalej, podobnie jak nasze przeglądarki. ta technika nie jest już zalecana i powinieneś użyć rozwiązania @ dana poniżej ( https://stackoverflow.com/a/7557433/5628 ) jeśli nie musisz obsługiwać IE

Oryginalne rozwiązanie (obecnie przestarzałe):

To sprawdzi, czy element jest całkowicie widoczny w bieżącym widoku:

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

Możesz zmodyfikować to po prostu, aby określić, czy jakakolwiek część elementu jest widoczna w viewport:

function elementInViewport2(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top < (window.pageYOffset + window.innerHeight) &&
    left < (window.pageXOffset + window.innerWidth) &&
    (top + height) > window.pageYOffset &&
    (left + width) > window.pageXOffset
  );
}
 295
Author: Prestaul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:54

Teraz większość przeglądarek obsługuje metodę getBoundingClientRect , która stała się najlepszą praktyką. Używanie starej odpowiedzi jest bardzo powolne, nie dokładne i ma kilka błędów .

Rozwiązanie wybrane jako poprawne jest prawie nigdy precyzyjne. Możesz przeczytać więcej o jego błędach.


To rozwiązanie zostało przetestowane na IE7+, iOS5 + Safari, Android2+, Blackberry, Opera Mobile i 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() */
    );
}

Jak używać:

Możesz być pewien, że powyższa funkcja zwraca poprawną odpowiedź w momencie jej wywołania, ale co ze śledzeniem widoczności elementu jako zdarzenia?

Umieść poniższy kod na dole twojego znacznika <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);
}
*/

Jeśli zrobisz jakieś modyfikacje DOM, mogą one oczywiście zmienić widoczność twojego elementu.

Wytyczne i wspólne pułapki:

Może potrzebujesz śledzić powiększenie strony / uszczypnięcie urządzenia mobilnego? jQuery powinien obsługiwać zoom/pinchcross browser, w przeciwnym razie firstlub second link powinien ci pomóc.

Jeśli zmodyfikujesz DOM, może to wpłynąć na widoczność elementu. Powinieneś przejąć nad tym kontrolę i zadzwonić handler() ręcznie. Niestety, nie mamy zdarzenia cross browser onrepaint. Z drugiej strony, co pozwala nam na dokonywanie optymalizacji i wykonywanie sprawdź ponownie tylko modyfikacje DOM, które mogą zmienić widoczność elementu.

Nigdy przenigdy użyj go wewnątrz jQuery $(document).ready () Tylko, ponieważ nie ma gwarancji, że CSS został zastosowany w tym momencie. Twój kod może działać lokalnie z Twoim CSS na dysku twardym, ale po umieszczeniu na zdalnym serwerze zawiedzie.

Po wywołaniu DOMContentLoaded, style są stosowane , ale obrazy nie są jeszcze ładowane. Warto więc dodać window.onload event słuchacz.

Nie możemy jeszcze złapać zdarzenia zoom/pinch.

Ostatnią deską ratunku może być następujący kod:

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

Możesz użyć awesome feature pageVisibiliy HTML5 API, jeśli zależy Ci, czy karta ze stroną jest aktywna i widoczna.

TODO: ta metoda nie obsługuje dwóch sytuacji:

 1190
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:59

Update

W nowoczesnych przeglądarkach warto sprawdzić API Intersection Observer , które zapewnia następujące korzyści:

    [[14]}Lepsza wydajność niż słuchanie zdarzeń przewijania
  • Działa w cross domain iframes
  • może stwierdzić, czy element blokuje / przecinając inny

Intersection Observer jest na drodze do bycia pełnoprawnym standardem i jest już obsługiwany w Chrome 51+, Edge 15 + i Firefox 55+ i jest w trakcie tworzenia dla Safari. Dostępny jest również polyfill.


Poprzednia odpowiedź

Istnieją pewne problemy z odpowiedzi udzielonej przez Dana , które mogą sprawić, że będzie to nieodpowiednie podejście do niektórych sytuacji. Niektóre z tych problemów są wskazywane w jego odpowiedzi na dole, że jego kod daje fałszywe alarmy dla elementów, które są: {]}

  • ukryty przez inny element przed testowanym
  • poza widzialnym obszar elementu nadrzędnego lub nadrzędnego
  • element lub jego dzieci ukryte przy użyciu właściwości CSS clip

Ograniczenia te są wykazane w następujących wynikach prostego testu :

Nieudany test przy użyciu isElementInViewport

Rozwiązanie: isElementVisible()

Oto rozwiązanie tych problemów, z wynikiem testu poniżej i wyjaśnieniem niektórych części kodu.

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

Test zaliczeniowy: http://jsfiddle.net/AndyE/cAY8c/

I wynik:

Zdany test, przy użyciu isElementVisible

Uwagi dodatkowe

Metoda ta nie jest jednak pozbawiona własnych ograniczeń. Na przykład, element testowany z niższym indeksem z niż inny element w tym samym miejscu zostanie zidentyfikowany jako ukryty, nawet jeśli element z przodu nie ukrywa żadnej jego części. Mimo to metoda ta ma swoje zastosowania w niektórych przypadkach, których rozwiązanie dana nie obejmuje.

Obie element.getBoundingClientRect() i document.elementFromPoint() są częścią roboczej specyfikacji CSSOM i są obsługiwane przez co najmniej IE 6 i nowsze oraz większość przeglądarek desktopowych przez długi czas (choć nie idealnie). Zobacz Quirksmode na tych funkcjach aby uzyskać więcej informacji.

contains() jest używany do sprawdzania, czy Element zwracany przez document.elementFromPoint() jest węzłem potomnym elementu, który testujemy pod kątem widoczności. Zwraca również true, jeśli zwracany element jest tym samym elementem. To tylko sprawia, że czek jest bardziej wytrzymały. On obsługiwane we wszystkich głównych przeglądarkach, Firefox 9.0 jest ostatnią z nich, aby go dodać. Aby uzyskać obsługę starszego Firefoksa, Sprawdź historię tej odpowiedzi.

Jeśli chcesz sprawdzić więcej punktów wokół elementu pod kątem widoczności―tj. aby upewnić się, że element nie jest pokryty więcej niż, powiedzmy, 50%―nie trzeba wiele, aby dostosować ostatnią część odpowiedzi. Należy jednak pamiętać, że prawdopodobnie będzie to bardzo powolne, jeśli sprawdzisz każdy piksel, aby upewnić się, że jest w 100% widoczny.

 132
Author: Andy E,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-13 12:08:50

Próbowałem odpowiedz dana ale algebra używana do wyznaczania granic jest niepoprawna. odpowiedź ryanve ' a jest bliższa, ale badany element powinien znajdować się w widoku o co najmniej 1 piksel, więc spróbuj tej funkcji:

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() */;
}
 46
Author: Walf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-01 06:31:52

Jako usługa publiczna:
Odpowiedź dana z poprawnymi obliczeniami (element może być > window, szczególnie na ekranach telefonów komórkowych) i poprawnym testowaniem jQuery, a także dodaniem isElementPartiallyInViewport:

Przy okazji, różnica między oknem.innerWidth i document.documentElement.clientWidth oznacza, że clientWidth/clientHeight nie zawiera paska przewijania, natomiast window.innerWidth / Height does.

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

Test-case

<!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>
 27
Author: Stefan Steiger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 13:06:05

Istnieje plugin jQuery o nazwie inview który wykonuje zadanie

 25
Author: Yuri Salimovskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-03 17:00:34

Zobacz źródło verge , które wykorzystuje getBoundingClientRect . Jest jak:

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

}

Zwraca true jeśli jakakolwiek część elementu znajduje się w widoku.

 24
Author: ryanve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-15 10:16:25

Moja krótsza i szybsza wersja.

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

Dodaj jsFiddle zgodnie z wymaganiami https://jsfiddle.net/on1g619L/1/

 20
Author: Eric Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-07 03:58:04

Uznałem za niepokojące, że nie ma jQuery centrycznej wersji funkcjonalności dostępnej. Kiedy natknąłem się na rozwiązanie dana śledziłem okazję, aby zapewnić coś dla ludzi, którzy lubią programować w stylu jQuery OO. Pamiętaj, aby przewinąć w górę i zostawić głos na temat kodu dana. Jest miły i zgryźliwy i działa jak urok dla mnie.

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

Użycie

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

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

});
 19
Author: r3wt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 20:06:53

Uważam, że przyjęta odpowiedź jest zbyt skomplikowana dla większości przypadków użycia. Ten kod dobrze wykonuje zadanie (używając JQuery) i rozróżnia elementy w pełni widoczne i częściowo widoczne.

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");
    }
});
 6
Author: Adam Rehal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-01 08:10:43

Myślę, że jest to bardziej funkcjonalny sposób. Odpowiedź dana nie działa w kontekście rekurencyjnym.

Ta funkcja rozwiązuje problem, gdy twój element znajduje się wewnątrz innych przewijalnych divów, testując dowolne poziomy rekurencyjnie wyższe do znacznika HTML i zatrzymując się w pierwszym 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)
    );
};
 3
Author: ton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-24 15:12:46

Zależy co masz na myśli mówiąc widoczny. Jeśli masz na myśli, czy jest on aktualnie wyświetlany na stronie, biorąc pod uwagę pozycję przewijania, możesz obliczyć go na podstawie przesunięcia elementów y i bieżącej pozycji przewijania.

 2
Author: roryf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 21:29:03

Na podstawie powyższego rozwiązania @ dan ( https://stackoverflow.com/a/7557433/5628 ), miałem okazję posprzątać implementację, aby korzystanie z niej wielokrotnie na tej samej stronie było łatwiejsze:

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

});

Sposób, w jaki go używam, polega na tym, że gdy element przewija się do widoku, dodaję klasę, która uruchamia animację klatki kluczowej css. Jest to dość proste i działa szczególnie dobrze, gdy masz ponad 10 rzeczy do warunkowego animowania na stronie.

Mam nadzieję, że to pomoże!
 2
Author: Pirijan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:18

Wszystkie odpowiedzi jakie tu napotkałem sprawdzają tylko czy element jest umieszczony wewnątrz bieżącego widoku . Ale to nie oznacza, że jest widoczny .
Co zrobić, jeśli dany element znajduje się wewnątrz div z przepełnioną treścią i jest przewijany poza widok?

Aby to rozwiązać, musisz sprawdzić, czy element jest zawarty przez wszystkich rodziców.
Moje rozwiązanie robi dokładnie to:

Pozwala również określić, ile elementu ma być widoczne.

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

To rozwiązanie zignorowało fakt, że elementy mogą nie być widoczne z powodu innych faktów, takich jak opacity: 0.

Przetestowałem To rozwiązanie w Chrome i Internet Explorer 11.

 2
Author: Domysee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-23 17:47:56

Oto moje rozwiązanie, będzie działać, jeśli element jest ukryty w kontenerze z możliwością przewijania.

Oto demo (spróbuj zmienić rozmiar okna na)

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

Musiałem tylko sprawdzić, czy jest widoczny w osi Y (dla przewijania ajax load more records feature).

 1
Author: Ally,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-07 13:03:13

Lepsze rozwiązanie:

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;    
}
 0
Author: rainyjune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-08 08:57:41

Sprawdza, czy element jest przynajmniej częściowo widoczny (wymiar pionowy):

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
Author: Lumic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-02 12:17:33

Miałem to samo pytanie i zorientowałem się, używając getBoundingClientRect (). Ten kod jest całkowicie 'ogólny' i musi być napisany tylko raz, aby zadziałał (nie musisz go zapisywać dla każdego elementu, który chcesz wiedzieć, jest w widoku). Ten kod sprawdza tylko, czy jest pionowo w widoku , a nie poziomo . W tym przypadku zmienna (tablica) 'elements' przechowuje wszystkie elementy, które sprawdzasz, aby były pionowo w widoku, więc chwyć dowolne elementy chcesz gdziekolwiek i przechowywać je tam. Pętla " for " przecina każdy element i sprawdza, czy znajduje się on pionowo w widoku. Ten kod wykonuje za każdym razem użytkownik przewija! Jeśli getBoudingClientRect ().góra jest mniejsza niż 3/4 viewport (element jest jedną czwartą w viewport), rejestruje się jako 'w viewport'. Ponieważ kod jest ogólny, będziesz chciał wiedzieć ,który element jest w widoku. Aby to sprawdzić, możesz określić go za pomocą niestandardowego atrybutu, nazwy węzła, id, Nazwa klasy i inne. Oto Mój kod (powiedz mi, jeśli nie działa, został przetestowany w IE 11, FireFox 40.0.3, Chrome w wersji 45.0.2454.85 m, Opera 31.0.1889.174 i krawędzi z Windows 10, [Jeszcze nie 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.');
   }
};

Mam nadzieję, że to komuś pomoże: -)

 0
Author: www139,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-13 13:59:36

Proste i małe rozwiązanie, które mi się udało.

Przykład chcesz sprawdzić, czy element jest widoczny w elemencie nadrzędnym, który ma przewijanie przepełnienia.

$(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');
     }
})
 0
Author: Stevan Tosic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-09 19:56:17

Oto funkcja, która mówi, czy element jest widoczny w bieżącym widoku elementu rodzica:

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
Author: ssten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-28 10:01:57

Używam tej funkcji (sprawdza tylko czy y jest inscreen ponieważ przez większość czasu x nie jest potrzebne)

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

}
 -1
Author: sanderjonk01,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-13 06:57:09

Na podobne wyzwanie naprawdę podobał mi sięTen gist , który eksponuje polyfill dlascrollIntoViewIfNeeded () .

Wszystkie niezbędne Kung Fu potrzebne do odpowiedzi znajdują się w tym bloku:

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 odnosi się do elementu, który chcesz wiedzieć, czy jest to np. overTop lub overBottom - po prostu powinien dostać dryf...

 -2
Author: Philzen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 13:14:21