Sie wissen es vielleicht nicht, aber JavaScript hat in letzter Zeit einige Beobachter heimlich angehäuft, und der Intersection Observer ist Teil dieses Arsenals. Observer sind Objekte, die in Echtzeit etwas erkennen – wie Vogelbeobachter, die zu ihrem Lieblingsplatz gehen, um auf Vögel zu warten.
Unterschiedliche Observer beobachten unterschiedliche Dinge (nicht jeder beobachtet Habichte).
Der allererste Observer, den ich kennengelernt habe, war der Mutation Observer, der nach Änderungen im DOM-Baum sucht. Damals war er einzigartig, aber jetzt haben wir viele weitere Observer.
Intersection Observer beobachtet die „Schnittmenge“ (d. h. das Vorbeiziehen) eines Elements durch eines seiner Elternelemente oder den Bereich auf dem Bildschirm, wo die Seite sichtbar ist (auch als Viewport bekannt).
Es ist ein bisschen so, als würde man einen Zug durch einen Bahnhof fahren sehen. Man kann sehen, wann der Zug einfährt, wann er abfährt und wie lange er stillstand.
Zu wissen, wann ein Element im Begriff ist, in den Sichtbereich zu gelangen, ob es aus dem Sichtbereich verschwunden ist oder wie lange es her ist, seit es in den Sichtbereich gelangt ist, hat alles nützliche Anwendungen. Wir werden also einige dieser Anwendungsfälle jetzt sehen – direkt nachdem wir den Code zum Erstellen eines IntersectionObserver-Objekts über die Intersection Observer API gesehen haben.
Ein kurzer Überblick über IntersectionObserver
Die Intersection Observer API hat zum Zeitpunkt des Schreibens bereits breite Unterstützung erfahren.
Diese Daten zur Browserunterstützung stammen von Caniuse, wo es weitere Details gibt. Eine Zahl bedeutet, dass der Browser die Funktion ab dieser Version unterstützt.
Desktop
| Chrome | Firefox | IE | Edge | Safari |
|---|---|---|---|---|
| 58 | 55 | Nein | 16 | 12.1 |
Mobil / Tablet
| Android Chrome | Android Firefox | Android | iOS Safari |
|---|---|---|---|
| 127 | 127 | 127 | 12.2-12.5 |
Wenn Sie jedoch prüfen möchten, ob Intersection Observer unterstützt wird, während Sie damit arbeiten, können Sie sehen, ob die Eigenschaft IntersectionObserver im window-Objekt vorhanden ist.
if(!!window.IntersectionObserver){}
/* or */
if('IntersectionObserver' in window){}
Okay, nun werfen wir einen Blick auf die Objekterstellung.
var observer = new IntersectionObserver(callback, options);
Der Konstruktor des IntersectionObserver-Objekts nimmt zwei Parameter entgegen. Der erste ist eine Callback-Funktion, die ausgeführt wird, sobald der Observer eine Schnittmenge bemerkt und asynchron einige Daten über diese Schnittmenge geliefert hat.
Der zweite (optionale) Parameter sind Optionen, ein Objekt mit Informationen zur Definition dessen, was die „Schnittmenge“ sein wird. Wir wollen vielleicht nicht wissen, wann ein Element im Begriff ist, in den Sichtbereich zu gelangen, sondern nur, wenn es *vollständig* sichtbar ist. So etwas wird über den Optionsparameter definiert.
Optionen hat drei Eigenschaften.
root– Das Elternelement/Viewport, mit dem das beobachtete Element eine Schnittmenge bildet. Stellen Sie sich das als den Bahnhof vor, mit dem der Zug eine Schnittmenge bildet.rootMargin– Ein Rand des Root-Elements, der den Bereich des Root-Elements verkleinert oder vergrößert, um nach Schnittmengen Ausschau zu halten. Ähnlich der CSS-Eigenschaftmargin.threshold– Ein Array von Werten (zwischen 0 und 1,0), die jeweils den Abstand angeben, den ein Element in den Root hineinragt oder darüber hinausgeht, bei dem der Callback ausgelöst werden soll.
Nehmen wir an, unser Schwellenwert ist 0,5. Der Callback wird ausgelöst, wenn das Element seinen halb sichtbaren Schwellenwert erreicht oder überschreitet. Wenn der Wert [0,3, 0,6] ist, wird der Callback ausgelöst, wenn das Element seinen 30% sichtbaren Schwellenwert erreicht oder überschreitet und auch seinen 60% sichtbaren Schwellenwert.
Genug Theorie für jetzt. Sehen wir uns einige Demos an. Zuerst das Lazy Loading.
Anwendungsfall 1: Lazy Loading von Bildern
Sehen Sie den Pen
Intersection Observer – Lazy Load von Preethi Sam (@rpsthecoder)
auf CodePen.
Um das Laden „auf den Punkt“ zu sehen, besuchen Sie diese Webseite, da die eingebettete Demo dies nicht zeigt.
CSS-Tricks hat Lazy Loading bereits gründlich behandelt, und normalerweise wird es so gemacht: Ein leichtgewichtiger Platzhalter wird dort angezeigt, wo Bilder sein sollen, und dann werden sie durch die beabsichtigten Bilder ersetzt, wenn sie in den Sichtbereich gelangen (oder kurz davor sind). Glauben Sie mir, es ist nichts Lazy an der Implementierung – bis wir etwas Natives zum Arbeiten haben.
Wir wenden die gleiche Mechanik an. Zuerst haben wir eine Reihe von Bildern und einen Platzhalterbild definiert, das anfänglich angezeigt wird. Wir verwenden ein Datenattribut, das die URL des ursprünglichen Bildes enthält, das angezeigt werden soll, und das das tatsächliche Bild definiert, das geladen wird, wenn es in den Sichtbereich gelangt.
<img src="placeholder.png" data-src="img-1.jpg">
<img src="placeholder.png" data-src="img-2.jpg">
<img src="placeholder.png" data-src="img-3.jpg">
<!-- more images -->
Der Rest ist Skripting.
let observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
/* Here's where we deal with every intersection */
});
},
{rootMargin: "0px 0px -200px 0px"});
Die obige Callback-Funktion ist eine Pfeilfunktion (Sie können aber auch eine normale Funktion verwenden).
Die Callback-Funktion empfängt zwei Parameter: eine Reihe von Einträgen (entries) mit Informationen über jede Schnittmenge und den Observer selbst. Diese Einträge können gefiltert oder durchlaufen werden, damit wir uns mit den gewünschten Schnittmengen befassen können. Was die Optionen betrifft, so habe ich nur den Wert rootMargin angegeben und die Eigenschaften root und threshold auf ihre Standardwerte fallen gelassen.
Der Standardwert für root ist der Viewport und der Standardwert für threshold ist 0 – was grob übersetzt werden kann als „Benachrichtige mich, sobald das Element im Viewport erscheint!“
Das Seltsame ist jedoch, dass ich den Beobachtungsbereich des Viewports um zweihundert Pixel am unteren Rand mit rootMargin reduziert habe. Normalerweise würden wir das beim Lazy Loading nicht tun. Stattdessen könnten wir den Rand vergrößern oder ihn auf dem Standardwert belassen. Aber eine Verringerung ist in dieser Situation nicht üblich. Ich habe es nur getan, weil ich zeigen möchte, wie die Originalbilder am Schwellenwert im beobachteten Bereich geladen werden. Andernfalls würde die gesamte Aktion außerhalb des Sichtbereichs stattfinden.
Wenn das Bild mit dem Beobachtungsbereich des Viewports (der 200 Pixel von unten im Demo ist) eine Schnittmenge bildet, ersetzen wir das Platzhalterbild durch das tatsächliche Bild.
let observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
/* Placeholder replacement */
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
});
},
{rootMargin: "0px 0px -200px 0px"});
entry.target ist das Element, das vom Observer beobachtet wird. In unserem Fall sind das die Bildelemente. Sobald der Platzhalter in einem Bildelement ersetzt wurde, müssen wir es nicht mehr beobachten, also rufen wir die Methode unobserve des Observers für dieses Element auf.
Jetzt, da der Observer bereit ist, ist es Zeit, alle Bilder mit der Methode observer zu beobachten.
document.querySelectorAll('img').forEach(img => { observer.observe(img) });
Das war's! Wir haben die Bilder lazy geladen. Weiter geht's mit der nächsten Demo.
Anwendungsfall 2: Video automatisch pausieren, wenn es aus dem Sichtbereich verschwindet
Nehmen wir an, wir schauen uns ein Video auf YouTube an und wollen (aus welchem Grund auch immer) nach unten scrollen, um die Kommentare zu lesen. Ich weiß nicht, wie es Ihnen geht, aber normalerweise pausiere ich das Video nicht, bevor ich das tue, was bedeutet, dass ich einen Teil des Videos verpasse, während ich surfe.
Wäre es nicht schön, wenn das Video für uns pausieren würde, wenn wir wegscrollen? Es wäre noch schöner, wenn das Video wieder abspielen würde, wenn es wieder im Sichtbereich ist, sodass man nicht einmal auf Play oder Pause drücken muss.
Intersection Observer kann das auf jeden Fall.
Sehen Sie den Pen
IntersectionObserver: Video außerhalb des Sichtbereichs automatisch pausieren von Preethi Sam (@rpsthecoder)
auf CodePen.
Hier ist unser Video im HTML.
<video src="OSRO-animation.mp4" controls=""></video>
So schalten wir zwischen Wiedergabe und Pause um.
let video = document.querySelector('video');
let isPaused = false; /* Flag for auto-paused video */
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio!=1 && !video.paused){
video.pause(); isPaused = true;
}
else if(isPaused) {video.play(); isPaused=false}
});
}, {threshold: 1});
observer.observe(video);
Bevor ich Ihnen zeige, wie wir das Video bei jeder Schnittmenge (d. h. jedem Eintrag) pausieren und abspielen, möchte ich Ihre Aufmerksamkeit auf die Eigenschaft threshold der Optionen lenken.
Der threshold hat den Wert 1. Sowohl root als auch rootMargin werden standardmäßig verwendet. Das bedeutet, „Hey, lass mich wissen, sobald das Element *vollständig* im Viewport sichtbar ist.“
Sobald die Schnittmenge eintritt und der Callback ausgelöst wird, pausieren oder spielen wir das Video basierend auf dieser Logik.

Ich habe unobserve für das Video nicht aufgerufen, daher beobachtet der Observer das Video weiterhin und pausiert *jedes Mal*, wenn es aus dem Sichtbereich verschwindet.
Anwendungsfall 3: Sehen, wie viel Inhalt angesehen wurde
Dies kann auf viele Arten interpretiert und implementiert werden, abhängig davon, was Ihre Inhalte sind und wie Sie messen möchten, wie viel davon angesehen wurde.
Als einfaches Beispiel beobachten wir den letzten Absatz jedes Artikels in einer Liste von Artikeln auf einer Seite. Sobald der letzte Absatz eines Artikels vollständig sichtbar wird, betrachten wir diesen Artikel als gelesen – ähnlich wie wir sagen könnten, dass das Sehen des letzten Waggons eines Zuges bedeutet, den ganzen Zug gesehen zu haben.
Hier ist ein Demo, das zwei Artikel auf einer Seite zeigt, die jeweils eine Reihe von Absätzen enthalten.
Sehen Sie den Pen
IntersectionObserver: Inhalt angesehen von Preethi Sam (@rpsthecoder)
auf CodePen.
Unser vereinfachtes HTML sieht etwa so aus.
<div id="count"><!-- The place where "number of articles viewed" is displayed --></div>
<h2>Article 1</h2>
<article>
<p><!-- Content --></p>
<!-- More paragraphs -->
</article>
<h2>Article 2</h2>
<article>
<p><!-- Content --></p>
<!-- More paragraphs -->
</article>
<!-- And so on... -->
let n=0; /* Total number of articles viewed */
let count = document.querySelector('#count');
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.isIntersecting){
count.textContent= `articles fully viewed - ${++n}`;
observer.unobserve(entry.target);
}
});
}, {threshold: 1});
document.querySelectorAll('article > p:last-child').forEach(p => { observer.observe(p) });
Bei jeder Schnittmenge – der vollständigen Ansicht des letzten Absatzes eines Artikels – erhöhen wir einen Zähler: n, der die Gesamtzahl der gelesenen Artikel darstellt. Dann zeigen wir diese Zahl über der Liste der Artikel an.
Sobald wir eine Schnittmenge des letzten Absatzes gezählt haben, muss dieser nicht mehr beobachtet werden, also rufen wir unobserve dafür auf.
Danke fürs Zuschauen!
Das war's für die Beispiele, die wir uns gemeinsam in diesem Beitrag ansehen werden. Sie haben wahrscheinlich die Idee, wie es funktioniert, Elemente zu beobachten und Ereignisse auszulösen, basierend darauf, wo sie mit dem Viewport in Schnittmenge treten.
Das gesagt, ist es ratsam, vorsichtig zu sein, wenn visuelle Änderungen auf der Grundlage der durch den Observer erhaltenen Schnittmengen-Daten vorgenommen werden. Sicher, Intersection Observer ist problemlos, wenn es darum geht, Schnittmengen-Daten zu *protokollieren*. Aber wenn er verwendet wird, um Änderungen auf dem Bildschirm vorzunehmen, müssen wir sicherstellen, dass die Änderungen nicht verzögert sind, was möglich ist, da wir im Grunde genommen Änderungen auf der Grundlage von Daten vornehmen, die *asynchron* abgerufen werden. Das kann etwas Zeit zum Laden benötigen.
Wie wir gesehen haben, hat jeder Schnittmengen-Eintrag eine Reihe von Eigenschaften, die Informationen über die Schnittmenge vermitteln. Ich habe nicht alle davon in diesem Beitrag behandelt, also lesen Sie sie sich unbedingt durch.
Habe einen seltsamen Glitch für Anwendungsfall Nr. 3 gefunden, während er unter Linux in Firefox und Chrome sowie unter Android in Firefox gut funktioniert, schlägt er fehl, den ersten Artikel in Chrome auf meinem Android-Handy zu zählen. Zeigt nur „Artikel vollständig angesehen – 1“ an, wenn Sie das Ende des zweiten Artikels erreichen.
Danke!!!
Die einzige „threshold“-Option funktioniert bei mir in Edge (vor der Chromium-Ära) nicht.
Sie verhält sich immer so, als wäre sie „threshold: 0“, unabhängig vom eingestellten Wert, was oft dazu führt, dass der Callback früher als erwartet ausgeführt wird. Gelöst durch Hinzufügen einer zusätzlichen Prüfung, ob „intersectionRatio“ mit „threshold“ übereinstimmt.
Ja. Caniuse gibt an, dass die threshold-Option in MS Edge nicht unterstützt wird (und die isIntersecting-Flagge wahrscheinlich aus demselben Grund nicht). Die Verwendung von intersectionRatio ist die empfohlene Umgehung.
Das ist nützlich, danke! Wo finde ich ein fertiges, funktionierendes Beispiel?
Danke für die Erklärung! Sehr nützlich.
Wie würde der Code in #use case2 für ein paar Videos auf derselben Seite aussehen?
Ich habe query.SelectorAll versucht, aber ohne Erfolg.
Sehr informativ! Danke für diesen Artikel.
Was ist mit mehreren Zielen auf einer Seite?
Gibt es eine Möglichkeit, die Anzahl der sichtbaren Objekte/Ziele in einem bestimmten Bereich zu zählen?
Nochmals vielen Dank!