Frage Wie erkennt man, ob ein DOM-Element im aktuellen Ansichtsfenster sichtbar ist?


Gibt es eine effiziente Möglichkeit festzustellen, ob ein DOM-Element (in einem HTML-Dokument) derzeit sichtbar ist (erscheint in der Ansichtsfenster)

(Die Frage betrifft Firefox)


747
2017-09-23 21:24


Ursprung


Antworten:


Aktualisieren: Die Zeit marschiert weiter und so haben unsere Browser. Diese Technik wird nicht mehr empfohlen und Sie sollten @ Dans Lösung unten verwenden (https://stackoverflow.com/a/7557433/5628) wenn Sie IE <7 nicht unterstützen müssen.

Originallösung (jetzt veraltet):

Dadurch wird überprüft, ob das Element im aktuellen Ansichtsfenster vollständig sichtbar ist:

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

Sie können dies einfach ändern, um festzustellen, ob ein Teil des Elements im Ansichtsfenster sichtbar ist:

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

286
2017-09-24 02:40



Jetzt die meisten Browser Unterstützung getBoundingClientRect Methode, die zur besten Praxis geworden ist. Mit einer alten Antwort ist sehr langsam, nicht genau und hat mehrere Fehler.

Die gewählte Lösung ist korrekt fast nie genau. Sie können Weiterlesen über seine Fehler.


Diese Lösung wurde auf IE7 +, iOS5 + Safari, Android2 +, Blackberry, Opera Mobile und IE Mobile getestet 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() */
    );
}

Wie benutzt man:

Sie können sicher sein, dass die oben angegebene Funktion die richtige Antwort zum Zeitpunkt des Aufrufs zurückgibt, aber was ist mit der Sichtbarkeit des Elements als Ereignis?

Platzieren Sie den folgenden Code an der Unterseite Ihres <body> Etikett:

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

Wenn Sie Änderungen am DOM vornehmen, können sie natürlich die Sichtbarkeit Ihres Elements ändern.

Richtlinien und allgemeine Fallstricke:

Vielleicht müssen Sie den Seitenzoom / die Prise für mobile Geräte verfolgen? jQuery sollte handhaben Zoom / Prise Cross-Browser, sonst zuerst oder zweite Link sollte dir helfen.

Wenn du DOM ändern, kann es die Sichtbarkeit des Elements beeinflussen. Sie sollten die Kontrolle übernehmen und anrufen handler() manuell. Leider haben wir keinen Cross-Browser onrepaint Veranstaltung. Auf der anderen Seite können wir so Optimierungen vornehmen und nur DOM-Modifikationen überprüfen, die die Sichtbarkeit des Elements verändern können.

Niemals benutze es in jQuery $ (Dokument) .ready () nur weil Es gibt keine Garantie CSS wurde angewendet in diesem Moment. Ihr Code kann lokal mit Ihrem CSS auf der Festplatte arbeiten, aber sobald er auf den Remote-Server gestellt wird, wird er fehlschlagen.

Nach DOMContentLoaded gefeuert wird, Stile werden angewendet, aber Die Bilder sind noch nicht geladen. Also sollten wir hinzufügen window.onload Ereignis-Listener.

Wir können das Zoom / Pinch-Ereignis noch nicht erfassen.

Der letzte Ausweg könnte der folgende Code sein:

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

Sie können tolle Funktion verwenden pageVisibiliy HTML5-API, wenn es Ihnen wichtig ist, ob der Tab mit Ihrer Webseite aktiv und sichtbar ist.

TODO: Diese Methode behandelt nicht zwei Situationen:


1163
2018-03-04 14:18



Aktualisieren

In modernen Browsern möchten Sie vielleicht die Intersection Observer API Das bietet die folgenden Vorteile:

  • Bessere Leistung als das Abhören von Scroll-Events
  • Funktioniert in domainübergreifenden Iframes
  • Kann erkennen, ob ein Element eine andere blockiert / schneidet

Intersection Observer ist auf dem Weg, ein vollwertiger Standard zu werden und wird bereits in Chrome 51+, Edge 15+ und Firefox 55+ unterstützt und wird für Safari entwickelt. Es gibt auch ein Polyfill verfügbar.


Vorherige Antwort

Es gibt einige Probleme mit dem Antwort von Dan das könnte es für einige Situationen ungeeignet machen. Einige dieser Probleme werden in seiner Antwort auf der unteren Seite darauf hingewiesen, dass sein Code falsche positive Ergebnisse für Elemente liefert, die:

  • Versteckt durch ein anderes Element vor dem getesteten
  • Außerhalb des sichtbaren Bereichs eines Eltern- oder Vorfahrenelements
  • Ein Element oder seine untergeordneten Elemente werden mithilfe des CSS ausgeblendet clip Eigentum

Diese Einschränkungen werden in den folgenden Ergebnissen von a gezeigt einfacher Test:

Failed test, using isElementInViewport

Die Lösung: isElementVisible()

Hier ist eine Lösung für diese Probleme, mit dem Testergebnis unten und einer Erläuterung einiger Teile des Codes.

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

Bestanden test:  http://jsfiddle.net/AndyE/cAY8c/

Und das Ergebnis:

Passed test, using isElementVisible

Zusätzliche Bemerkungen

Diese Methode ist jedoch nicht ohne eigene Einschränkungen. Zum Beispiel würde ein Element, das mit einem niedrigeren Z-Index als ein anderes Element an der gleichen Stelle getestet wird, als verborgen identifiziert, selbst wenn das Element davor keinen Teil davon tatsächlich verdeckt. Diese Methode findet jedoch in einigen Fällen Anwendung, die von Dans Lösung nicht abgedeckt werden.

Beide element.getBoundingClientRect() und document.elementFromPoint() sind Teil der CSSOM Working Draft Spezifikation und werden mindestens in IE 6 und höher unterstützt die meisten Desktop-Browser für eine lange Zeit (wenn auch nicht perfekt). Sehen Quirksmode über diese Funktionen für mehr Informationen.

contains() wird verwendet, um zu sehen, ob das Element von zurückgegeben wird document.elementFromPoint() ist ein Kindknoten des Elements, das wir auf Sichtbarkeit testen. Es gibt auch true zurück, wenn das zurückgegebene Element dasselbe Element ist. Dies macht die Überprüfung nur robuster. Es wird in allen gängigen Browsern unterstützt, wobei Firefox 9.0 der letzte ist, der es hinzufügt. Für ältere Firefox-Unterstützung, überprüfen Sie den Verlauf dieser Antwort.

Wenn Sie mehr Punkte um das Element für die Sichtbarkeit testen möchten - dh um sicherzustellen, dass das Element nicht von mehr als 50% abgedeckt wird - würde es nicht viel Zeit kosten, den letzten Teil der Antwort anzupassen. Beachten Sie jedoch, dass es wahrscheinlich sehr langsam wäre, wenn Sie jedes Pixel überprüfen, um sicherzustellen, dass es zu 100% sichtbar ist.


120
2018-04-29 02:36



Ich habe es versucht Dans Antwort  aber Die zur Bestimmung der Grenzen verwendete Algebra ist falsch. Ryanves Antwort ist näher, aber das zu testende Element sollte mindestens um 1 Pixel im Ansichtsfenster liegen. Probieren Sie diese Funktion aus:

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

43
2018-05-03 17:00



Es gibt jQuery-Plugin aufgerufen im Hinblick auf das macht den Job


25
2017-09-25 12:54



Als öffentlicher Dienst:
Dans Antwort mit den richtigen Berechnungen (Element kann> Fenster sein, besonders auf Handy-Bildschirmen), und korrekte jQuery-Tests, sowie das Hinzufügen von isElementPartiallyInViewport:

Übrigens, die Unterschied zwischen window.innerWidth und document.documentElement.clientWidth ist clientWidth / clientHeight nicht die Bildlaufleiste, während window.innerWidth / Height tut.

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

Testfall

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

25
2017-09-14 05:49



Siehe die Quelle von Rand, die verwendet getBoundingClientRect. Es ist wie:

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

}

Kehrt zurück true ob irgendein ein Teil des Elements befindet sich im Ansichtsfenster.


23
2017-08-02 13:40



Ich fand es beunruhigend, dass es keins gab jQuery zentrische Version der verfügbaren Funktionalität. Als ich herüberkam Dans Lösung Ich habe die Gelegenheit ausprobiert, etwas für Leute zu bieten, die gerne in der jQuery OO-Stil. Achten Sie darauf, nach oben zu scrollen und einen Kommentar zu Dans Code zu hinterlassen. Es ist schön und schnell und funktioniert wie ein Zauber für mich.

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

Verwendung

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

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

});

19
2018-04-23 03:04



meine kürzere und schnellere Version.

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

Fügen Sie nach Bedarf jsFiddle hinzu https://jsfiddle.net/on1g619L/1/


18
2018-01-30 14:49