1268 Stimmen

Wie kann ich feststellen, ob ein DOM-Element im aktuellen Ansichtsfenster sichtbar ist?

Gibt es eine effiziente Methode, um festzustellen, ob ein DOM-Element (in einem HTML-Dokument) derzeit sichtbar ist (erscheint in der Ansichtsfenster )?

(Die Frage bezieht sich auf Firefox.)

1 Stimmen

Es kommt darauf an, was Sie unter sichtbar verstehen. Wenn Sie meinen, dass es derzeit auf der Seite angezeigt wird, können Sie es anhand des y-Offsets des Elements und der aktuellen Bildlaufposition berechnen.

1 Stimmen

Ich habe hinzugefügt meine eigene Lösung die dieses Problem löst

1 Stimmen

Berücksichtigt eine dieser Lösungen den Z-Index eines Dom-Knotens und wie das die Sichtbarkeit speziell durch möglicherweise Ausblenden von Elementen mit einem niedrigeren Z-Index beeinflussen könnte?

1698voto

Dan Punkte 51805

Jetzt die meisten Browser Unterstützung getBoundingClientRect Methode, die sich zur besten Praxis entwickelt hat. Die Verwendung einer alten Antwort ist sehr langsam, nicht korrekt y hat mehrere Bugs .

Die als richtig ausgewählte Lösung lautet fast nie präzise .


Diese Lösung wurde mit Internet Explorer 7 (und höher), iOS 5 (und höher) und Safari getestet, Android 2.0 (Eclair) und höher, BlackBerry, Opera Mobile und Internet Explorer Mobile 9 .


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 zu verwenden:

Sie können sicher sein, dass die oben angegebene Funktion zum Zeitpunkt ihres Aufrufs eine korrekte Antwort liefert, aber was ist mit der Verfolgung der Sichtbarkeit des Elements als Ereignis?

Fügen Sie den folgenden Code am Ende Ihrer <body> Tag:

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); // Internet Explorer 9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);
}
*/

Wenn Sie irgendwelche DOM-Änderungen vornehmen, können diese natürlich die Sichtbarkeit Ihres Elements verändern.

Leitlinien und häufige Fallstricke:

Vielleicht müssen Sie den Seitenzoom / das Einklemmen von Mobilgeräten verfolgen? jQuery sollte behandeln vergrößern/verkleinern browserübergreifend, sonst erste o zweite Link sollte Ihnen helfen.

Wenn Sie DOM ändern kann sie die Sichtbarkeit des Elements beeinträchtigen. Sie sollten die Kontrolle darüber übernehmen und die handler() manuell. Leider haben wir keine browserübergreifenden onrepaint Ereignis. Andererseits können wir so Optimierungen vornehmen und nur bei DOM-Änderungen, die die Sichtbarkeit eines Elements verändern können, eine erneute Prüfung durchführen.

Niemals innerhalb von jQuery verwenden $(document).ready() nur, weil es wurde keine Garantie CSS angewendet in diesem Moment. Ihr Code kann lokal mit Ihrem CSS auf einer Festplatte funktionieren, aber sobald er auf einem entfernten Server liegt, wird er fehlschlagen.

Nach DOMContentLoaded abgefeuert wird, Stile angewendet werden pero die Bilder sind noch nicht geladen . Wir sollten also hinzufügen window.onload Ereignishörer.

Wir können das Ereignis Zoom/Pinch 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 die großartige Funktion nutzen pageVisibiliy der HTML5-API, wenn Sie sich dafür interessieren, ob die Registerkarte mit Ihrer Webseite aktiv und sichtbar ist.

TODO: Diese Methode kann zwei Situationen nicht behandeln:

7 Stimmen

Ich verwende diese Lösung (achten Sie allerdings auf den Tippfehler "botom"). Es gibt auch etwas zu beachten, wenn das Element, das wir in Betracht ziehen, Bilder in ihm haben würde. Chrome (zumindest) muss warten, bis das Bild geladen ist, um den genauen Wert für das boundingRectangle zu erhalten. Scheint, dass Firefox dieses "Problem" nicht hat

1 Stimmen

Window.innerHeight und innerWidth sind im IE undefiniert. Es gibt einen guten Artikel unter howtocreate.de/tutorials/javascript/browserwindow zeigt die verschiedenen Möglichkeiten, die Dokumenthöhe zu erhalten... document.body.clientHeight scheint der beste Weg insgesamt zu sein. Ich habe Ihre Antwort entsprechend bearbeitet.

0 Stimmen

Hier ist meine Version der Prüfung, sie prüft nur, ob das Element in vp überhaupt sichtbar ist: gist.github.com/2300296

422voto

Prestaul Punkte 79893

Aktualisierung: Die Zeit schreitet voran, und das gilt auch für unsere Browser. Diese Technik wird nicht mehr empfohlen und Sie sollten Dans Lösung wenn Sie keine Version von Internet Explorer vor 7 unterstützen müssen.

Ursprüngliche Lösung (jetzt veraltet):

Damit wird geprü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
  );
}

1 Stimmen

Die ursprünglich gepostete Funktion hatte einen Fehler. Musste die Breite/Höhe vor der Neuzuweisung von El...

0 Stimmen

Es könnte auch ratsam sein, dies ein wenig zu abstrahieren und einige Hilfsfunktionen zu erstellen. Ich verwende eine Funktion namens getViewport, die die obere/linke/untere/rechte Seite des sichtbaren Fensters zurückgibt, und eine Funktion namens getPosition, die die obere/linke Seite eines Elements ermittelt.

0 Stimmen

Ich versuche, diese Funktionalität in einer Firefox-Erweiterung zu erhalten, also nur für den Fall, dass jemand anderes in der gleichen Situation ist... (Web-Entwickler, können Sie natürlich diesen Kommentar vollständig ignorieren) Diese Funktion funktioniert perfekt in diesem Kontext, AUSSER, dass window verweist auf das falsche Objekt, also setzen Sie einfach var window = el.ownerDocument.defaultView am Anfang der Funktion, und schon kann es losgehen.

240voto

Andy E Punkte 324972

Update

In modernen Browsern sollten Sie sich die Schnittpunkt Beobachter API die die folgenden Vorteile bietet:

  • Bessere Leistung als beim Abhören von Bildlaufereignissen
  • Funktioniert in domänenübergreifenden iframes
  • Kann feststellen, ob ein Element ein anderes behindert/überschneidet

Intersection Observer ist auf dem Weg zu einem vollwertigen Standard und wird bereits von Chrome 51+, Edge 15+ und Firefox 55+ unterstützt und ist für Safari in Entwicklung. Es gibt auch eine Polyfill verfügbar.


Vorherige Antwort

Es gibt einige Probleme mit dem Antwort von Dan die ihn für manche Situationen ungeeignet machen könnten. Einige dieser Probleme werden in seiner Antwort in der Nähe des Bodens darauf hingewiesen, dass sein Code falsch-positive Ergebnisse für Elemente liefert, die sind:

  • Verdeckt durch ein anderes Element vor dem zu prüfenden Element
  • Außerhalb des sichtbaren Bereichs eines übergeordneten Elements oder eines Vorgängerelements
  • Ein Element oder seine untergeordneten Elemente, die mit dem CSS clip Eigenschaft

Diese Einschränkungen werden in den folgenden Ergebnissen einer 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 Erklärung einiger Teile des Codes.

function isElementVisible(el) {
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || document.documentElement.clientWidth,
        vHeight  = window.innerHeight || document.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))
    );
}

Bestehen der Prüfung: http://jsfiddle.net/AndyE/cAY8c/

Und das Ergebnis:

Passed test, using isElementVisible

Zusätzliche Hinweise

Diese Methode hat jedoch auch ihre Grenzen. So würde beispielsweise ein Element, das mit einem niedrigeren z-Index als ein anderes Element an derselben Stelle getestet wird, als ausgeblendet identifiziert werden, auch wenn das davor liegende Element tatsächlich keinen Teil davon verdeckt. Dennoch hat diese Methode in einigen Fällen ihre Berechtigung, die Dans Lösung nicht abdeckt.

Beide element.getBoundingClientRect() y document.elementFromPoint() sind Teil der CSSOM Working Draft-Spezifikation und werden mindestens von IE 6 und höher unterstützt. Die meisten Desktop-Browser für eine lange Zeit (wenn auch nicht perfekt). Siehe Quirksmode bei diesen Funktionen für weitere Informationen.

contains() wird verwendet, um zu sehen, ob das Element, das von document.elementFromPoint() ist ein untergeordneter Knoten des Elements, das wir auf Sichtbarkeit testen. Es gibt auch true zurück, wenn das zurückgegebene Element dasselbe Element ist. Dies macht die Prüfung einfach robuster. Sie wird von allen wichtigen Browsern unterstützt, wobei Firefox 9.0 der letzte von ihnen ist, der sie hinzufügt. Für ältere Firefox-Unterstützung, überprüfen Sie die Geschichte dieser Antwort.

Wenn Sie mehr Punkte um das Element herum auf Sichtbarkeit prüfen wollen, um sicherzustellen, dass das Element nicht zu mehr als, sagen wir, 50 % abgedeckt wird, wäre es nicht viel Aufwand, den letzten Teil der Antwort anzupassen. Beachten Sie jedoch, dass es wahrscheinlich sehr langsam wäre, wenn Sie jedes Pixel überprüfen würden, um sicherzustellen, dass es zu 100 % sichtbar ist.

3 Stimmen

Wollten Sie doc.documentElement.clientWidth verwenden? Sollte das stattdessen "document.documentElement" sein? Übrigens ist dies die einzige Methode, die auch für Anwendungsfälle wie das Ausblenden des Inhalts eines Elements für die Barrierefreiheit mit der CSS-Eigenschaft "clip" funktioniert: snook.ca/archives/html_and_css/hiding-content-for-accessibility

0 Stimmen

@klamping: Gut erkannt, danke. Ich hatte es direkt aus meinem Code kopiert, wo ich mit doc als Alias für document . Ja, ich halte das für eine vernünftige Lösung für die Grenzfälle.

0 Stimmen

Haben Sie eine Idee, wie man das z-index Problem beheben kann?

109voto

Ismail Farooq Punkte 5463

Wir haben jetzt ein natives javascript Schnittpunkt Beobachter API anhand derer wir erkennen können, ob sich Elemente im Ansichtsfenster befinden oder nicht.

Hier ein Beispiel

const el = document.querySelector('#el')
const observer = new window.IntersectionObserver(([entry]) => {
  if (entry.isIntersecting) {
    console.log('ENTER')
    return
  }
  console.log('LEAVE')
}, {
  root: null,
  threshold: 0.1, // set offset 0.1 means trigger if atleast 10% of element in viewport
})

observer.observe(el);

body {
  height: 300vh;
}

#el {
  margin-top: 100vh;
}

<div id="el">this is element</div>

21 Stimmen

Dies sollte als neue richtige Antwort ausgewählt werden.

1 Stimmen

Hervorragende Antwort

0 Stimmen

Die beste Lösung, die ich je benutzt habe, für diejenigen, die noch daran interessiert sind, hier ist es eingerichtet, um die Klasse zu entfernen const observer = new window.IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { entry.target.classList.add("viewport__active"); return; } entry.target.classList.remove("viewport__active"); }, { root: null, threshold: 0.4 // 0.0 - 1.0 } );

94voto

Walf Punkte 7428

Ich habe versucht Dans Antwort Die zur Bestimmung der Grenzen verwendete Algebra bedeutet jedoch, dass das Element sowohl die Größe des Ansichtsfensters haben als auch vollständig innerhalb des Ansichtsfensters liegen muss, um true was leicht zu falsch negativen Ergebnissen führen kann. Wenn Sie feststellen wollen, ob sich ein Element überhaupt im Ansichtsfenster befindet, ryanve's Antwort ist nahe dran, aber das getestete Element sollte das Ansichtsfenster überlappen, also versuchen Sie dies:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X