Intersection Observer

Lazy Loading Javascript Intersection Observer

Das Intersection Observer API beobachtet, ob (isIntersecting) und wie weit (Intersection Ratio) sich ein Element mit dem ViewPort überschneidet, um Inhalt erst zu laden, wenn Text, Bilder und iframes in den sichtbaren Sektor des ViewPorts kommen oder um ein Video zu starten oder zu pausieren.

Lazy Loading verzögert beim Laden der Seite Bilder, Videos und iFrames. So "stehen" Webseiten schneller und verhindern einen Datentransfer, der u.U. nicht benötigt wird.

18-12-15 SITEMAP

Scrollen und Rechnen: getBoundingClientRect

Der Mechanismus hinter dem Lazy Loading mit getBoundingClientRect und den meisten Plugins ist alt und kompatibel über die meisten Browser. Dabei wird laufend die Scroll-Position mit dem Abstand des Elements von der Grenze der Seite verglichen. Die permanenten Abfragen des DOM während des Scrollens sind eine Last, die schnell zu Ruckeln und Hakeln führt.

Intersection Observer

Der Intersection Observer beobachtet Änderungen eines Elements im Verhältnis zu einem Eltern-Element – meist zum Viewport. Wie weit überschneidet sich das Element mit einem umgebenden Element? Die Informationen über die Änderungen werden vor allem für das Nachladen von Inhalten beim Scrollen (z.B. lazy loading images) benutzt und allgemein zur Feststellung, dass ein Element in den Viewport kommt.

Der Aufruf new IntersectionObserver hat zwei Argumente: Das erste ist eine Callback-Funktion, die ausgeführt wird, sobald ein Element in den Viewport kommt oder wenn sich der Abstand zwischen Elementen um einen gewissen Betrag geändert hat. Damit muss die Position eines Elements in Hinsicht auf ein anderes Element beim Scrollen nicht mehr permanent abgefragt werden.

Das zweite Argument listet die Optionen für den Intersection Observer.

               callback function  ------+
                                        |
var io = new IntersectionObserver (handleEntries, options);
                                                     |
                                     Optionen  ------+
Lazy Loading mit IntersectionObserver

Im einfachsten Fall – Bild nachladen, wenn es in den Viewport kommt –, braucht der Intersection Observer nur wenige Zeilen.

<img src="blank.png" class="lazyload" data-src="img/DSC00532-l.jpg" alt="Lazy Loading mit IntersectionObserver">

var io = new IntersectionObserver ( (entry) => {
		console.log(entry);
		document.querySelector(".lazyload").src = document.querySelector(".lazyload").dataset.src;
	},
	{}
);

io.observe (document.querySelector(".lazyload"));

Die Optionen in diesem Beispiel sind leer ({}): Per Voreinstellung gilt der Viewport als Elternelement, das Bild wird angezeigt, wenn es im Viewport sichtbar ist.

Intersection Observer Options

Mit den Default-Optionen des IntersectionObserver wird die Callback-Funktion aufgerufen, wenn ein Element teilweise in den Viewport kommt und den ViewPort verläßt.

Das Observer API arbeitet nicht mit einem Pixelwert für das Überschneiden der Elemente, sondern reagiert, wenn die Überschneidung "so irgendwie" bei dem in Threshold angegebenen Prozentsatz liegt.

root
Das Elternelement oder der Viewport, in dem das Element liegt. null steht für den Viewport des Browsers.
threshold
ist ein Wert oder ein Array von Werten zwischen 0 und 1, bei denen der Intersection Observer die Callback-Funktion ausführt.
rootMargin
Beim Umfang des Überscheidungsbereichs (root) wird ein Abstand eingerechnet, ähnlich dem Margin bei CSS.

threshold = 0.5 würde beim Betreten und beim Verlassen des Root-Elements die Callback-Funktion aufrufen. Das könnte z.B. der Ablauf für ein Video sein, das bei Erscheinen im Viewport anläuft und pausiert, wenn es den Viewport verläßt.

RootRootRootRoot

Ist threshold ein Array – z.B. threshold: [0, 0.25, 0.5, 0.75, 1] –, wird die Callback-Funktion bei jedem der Werte aufgerufen.

Informationen des Intersection Observer

Ein Blick in die Konsole zeigt gleich beim Laden der Seite, dass sich das Element noch nicht mit dem root-Element überschneidet (intersectionRatio: 0) und isIntersecting steht noch auf false.

boundingClientRect: DOMRectReadOnly {x: 369, y: 55.0625, width: 900, height: 622, top: 55.0625, …}
intersectionRatio: 0
intersectionRect: DOMRectReadOnly {x: 369, y: 55.0625, width: 900, height: 621.9375, top: 55.0625, …}
isIntersecting: false
rootBounds: DOMRectReadOnly {x: 0, y: 0, width: 2019, height: 677, top: 0, …}
target: <div class="simple">
time: 22273

Sobald das Element in den sichtbaren Ausschnitt des Browserfensters kommt, ist intersectionRatio auf 1 gesetzt und isIntersecting true.

boundingClientRect: DOMRectReadOnly {x: 369, y: 51.0625, width: 900, height: 622, top: 51.0625, …}
intersectionRatio: 1
intersectionRect: DOMRectReadOnly {x: 369, y: 51.0625, width: 900, height: 622, top: 51.0625, …}
isIntersecting: true
rootBounds: DOMRectReadOnly {x: 0, y: 0, width: 2019, height: 677, top: 0, …}
target: <div class="simple">
time: 31279

Mehrere Bilder laden – z.B. Galerien

Wenn mehrere Elemente beobachtet werden sollen, sollten nach Möglichkeit alle Elemente vom selben IntersectionObserver durch mehrfache Aufrufe von observer () überwacht werden.

scene2-l
scene3-s
scene4-s
scene6-s
scene7-s
scene10-s
<img src="blank.png" data-src="deco-medium.jpg" width="480" height="480" alt="scene10-s">
…

const items = document.querySelectorAll(".item img");
const li = items.length;
var io = new IntersectionObserver ( function (entries) {	
	console.log (entries);
	for (let i=0; i<li; i++) {
		items[i].src = items[i].dataset.src;
	}
},{});

for (let i=0; i<items.length; i++) {
	io.observe (items[i]);
}

Wenn Bilder responsive mit HTML srcset eingebunden werden, kann die Callback-Funktion im IntersectionObserver die matchMedia-Abfrage einsetzen.

Relativ und fixed positionierte Intersection-Roots

Wenn Target-Elemente absolut innerhalb eines relativ positionierten Elements liegen, meldet der Intersection Observer keinen Treffer, wenn das Target-Element den relativ positionierten Root überschneidet.

Das gilt wohl auch für Target-Element mit position:fixed (?).

Browser ohne Javascript

Zwar sind Browser ohne Javascript zu einer Seltenheit geworden, aber wenn sie dennoch beachtet werden sollen, kann z.B. ein noscript-Tag eingesetzt werden. Auf HTML Living Standard gibt es bereits ein Attribut loading für img- und video-Tags, das zwei mögliche Werte annehmen kann: lazy und eager.