Bilder nachladen um die initiale Ladezeit zu verk├╝rzen

Fast jeder Entwickler oder Webseiten-Betreiber hat bereits von einem Lazy Loading gelesen oder aus Webentwickler-Kreisen geh├Ârt. In diesem Beitrag erkl├Ąre ich dir, welche Vorteile es hat Bilder nachzuladen und wie du das Script einfach auf deiner Webseite hinzuf├╝gen kannst.

Bitte beachte das Update vom 26.08.2019 am Ende des Beitrags.

Was ist "Lazy Loading"?

Es gibt viele Arten des Lazy-Loadings, wie zum Beispiel das Nachladen von JavaScript in WordPress. Es k├Ânnen auch Schriftarten, Icons oder andere Ressourcen auf der Webseite nachgeladen werden, um die Seite schneller aufrufbar zu machen. Das macht vor allem f├╝r mobile Besucher deiner Webseite Sinn, die mit einer niedrigeren Bandbreite surfen. Nat├╝rlich k├Ânnen Bilder erst nach einer kurzen Zeit angesehen werden, aber zumindest k├Ânnen die Texte schon einmal gelesen werden. Das wird vor allem von Google belohnt, weil der Besucher deine Seite seltener direkt wieder verlassen wird.

Bilder auf der Webseite nachladen lassen

Vielleicht hast du das Lazy Loading bereits auf der Startseite von Codepalm oder in der Beitrags-├ťbersicht gesehen. Bilder erscheinen erst nach einer sehr kurzen Zeit mit einem "Fading"-Effekt. Sollte das Bild bereits im Cache des Browsers liegen, wird der Fading-Effekt immer noch ausgef├╝hrt, jedoch erscheinen die Bilder wesentlich schneller.

Lass uns direkt in die Integration des Skripts einsteigen, damit sich die Performance deiner Webseite verbessert.

Benutzer-Erlebnis bei deaktiviertem JavaScript

Ein Hinweis vorweg: Das Lazy-Loading Script funktioniert nur f├╝r Benutzer, die JavaScript aktiviert haben. Sollte ein Benutzer einen Browser mit deaktiverten JavaScript verwenden, werden die Bilder gar nicht mehr ausgeliefert. Da die meisten Webseiten aber JavaScript dringend ben├Âtigen, empfehle ich dir einen Hinweis f├╝r Benutzer ausgeben zu lassen, der erkl├Ąrt, dass es aktiviert werden muss, um das beste Benutzer-Erlebnis f├╝r die Webseite zu garantieren. Mittlerweile ist die Verwendung von JavaScript so weit verbreitet, dass es nur noch einen verschwindend geringen Teil von Benutzern gibt, die JavaScript deaktivert haben.

Das JavaScript f├╝r das Lazy-Loading

Vorlage f├╝r das Lazy-Loading-Skripts

Die Vorlage des Skripts habe ich von Dean Hume, einen englisch-sprachigen Programmierer, der unter anderem an der Frostbite-Engine bei EA arbeitet. Er ist ein sehr passionierter Entwickler und hat bereits ein paar Lekt├╝ren ├╝ber das Web geschrieben. Das Lazy Loading Script von Dean Hume ist eher Kleinteilig beschrieben. Auf seiner Webseite erh├Ąltst du weitere Informationen zum Skript.

Das Lazy-Loading JavaScript

Zusammengefasst sieht das Skript so aus:

const images = document.querySelectorAll( '.lazy-load' );
const config = {
	rootMargin: '50px 0px',
	threshold: 0.01
};

let imageCount = images.length;
let observer;

if( !( 'IntersectionObserver' in window ) ) {
  loadImagesImmediately( images );
}
else {
	observer = new IntersectionObserver( onIntersection, config );
	for (let i = 0; i < images.length; i++) { 
		let image = images[i];
		if( image.classList.contains( 'js-lazy-image--handled' ) )
			continue;
		observer.observe( image );
	}
}

function fetchImage( url ) {
	return new Promise((resolve, reject) => {
		const image = new Image();
		image.src = url;
		image.onload = resolve;
		image.onerror = reject;
	});
}

function preloadImage( image ) {
	const src = image.dataset.src;
	if( !src )
		return;
	return fetchImage( src ).then(() => { applyImage( image, src ); });
}

function loadImagesImmediately( images ) {
	for( let i=0; i < images.length; i++ ) { 
		let image = images[i];
		preloadImage( image );
	}
}

function disconnect() {
	if( !observer )
		return;
	observer.disconnect();
}

function onIntersection( entries ) {
	if( imageCount === 0 ) {
		disconnect();
		return;
	}
	
	for( let i=0; i < entries.length; i++ ) {
		let entry = entries[i];
		if( entry.intersectionRatio > 0 ) {
			imageCount--;
			observer.unobserve( entry.target );
			preloadImage( entry.target );
		}
	}
}

function applyImage( img, src ) {
	img.classList.add( 'js-lazy-image--handled' );
	img.src = src;
	img.classList.add( 'fade-in' );
}

Dieses JavaScript kannst du kopieren und in deine Webseite einf├╝gen. Im besten Fall erstellst du dir eine eigene JS-Datei und f├╝gst es in jeder Einzelseite deiner Webseite ein. Die Implementierung funktioniert am besten ├╝ber einen globalen Header oder Footer.

Ein Hinweis zur Webseite von Dean Hume oder diesem Beitrag w├Ąre nett, ist aber nicht notwendig.

Nachzuladende Bilder platzieren

Durch das JavaScript erh├Ąlt man die M├Âglichkeit Bilder nachzuladen. Nun m├╝ssen wir den einzelnen Bildern noch beibringen, dass sie nachgeladen werden sollen. Das geschieht durch die Angabe der Klasse "lazy-load" im <img>-Tag.

Ein Transparentes Pixel als Platzhalter erstellen

Ein kleines Problemchen, das ich beim Script von Dean Hume entdeckt habe, ist das fehlende "src"-Attribut. Da der Wert aus dem "data-src"-Attribut in die "src" geladen wird, sobald der Besucher das Bild in den sichtbaren Bereich verschiebt, wird das Bild kurzzeitig als "fehlend" markiert. Um dieses Problem zu vermeiden, habe ich ein 1x1 Pixel gro├čes, transparentes PNG-Bild in das Base64-Format gebracht. Diese Ressource kann in das "src"-Attribut hinzugef├╝gt werden, damit das Bild nicht als "Broken Image" dargestellt wird.

Das ist der Code, um ein transparentes Pixel als src zu platzieren, ohne das Bild irgendwo auf dem Server gesichert zu haben:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABBJREFUeNpi+P//PwNAgAEACPwC/tuiTRYAAAAASUVORK5CYII=

In einem anderen Beitrag zeige ich, wie du Broken Image Elemente grafisch verbesserst. Das w├Ąre eine Alternative zur Verwendung des transparenten Base64-Pixels.

Einf├╝gen des Lazy-Loading Platzhalters

Das <img>-Tag wird in unserem HTML-Code nun leider etwas umfangreicher. Aber solange du beim Rendering und dem ersten Aufruf der Webseite Zeit sparst, solltest du dieses kleine ├ťbel in Kauf nehmen. Solltest du PHP f├╝r deine Webseite verwenden, kannst du das Base64-Bild in einer globalen Variable sichern und es in jedem src-Attribut des Bilds ausgeben lassen, oder eine eigene Funktion erstellen. Das empfehle ich den fortgeschritteneren Entwicklern ;)

Das fertige <img>-Tag kannst du dir gerne kopieren und durch alle Bilder auf deiner Webseite ersetzen. Stelle sicher, dass du den Wert des Attributs "data-src" mit dem entsprechenden Ressourcen-Pfad ├Ąnderst.

<img class="lazy-load" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABBJREFUeNpi+P//PwNAgAEACPwC/tuiTRYAAAAASUVORK5CYII=" data-src="/pfad/zum/bild.jpg" />

Source-Set im Lazy-Loading integrieren

Wenn du ein wenig Know-How mit JavaScript besitzt, kannst du das Skript auch relativ erweitern, um ein ganzes "srcset" eines Bilds zu erstellen, um verschiedenartige Darstellungsgr├Â├čen des Browsers mit unterschiedlich gro├čen Bildern abzudecken. Die einfachste Methode das zu erreichen, ist die Bearbeitung der vorletzten Zeile des JavaScript-Codes. Das img.src muss nur zu einem img.srcset ge├Ąndert werden. Beachte aber, dass du damit dann jedes Bild mit einem srcset-Attribut versiehst. Die Ausgabe der "src" wird dann eliminiert. Zudem musst du darauf achten, dass das Base64-Bild in das Attribut srcset im <img>-Tag eingef├╝gt wird, nicht mehr in das src-Attribut.

Kleine Randnotiz: Obwohl Google Pagespeed Insights die Bilder nicht in der Vorschau anzeigt, werden sie dennoch in der Google Bildersuche indexiert! Zum Gl├╝ck ist Google mittlerweile Intelligent genug, um JavaScript zu verwenden :)

 

Bilder erscheinen lassen

 

Fading Effekt beim nachladen von Bildern

Eine weitere kleine Erweiterung, die ich am JavaScript vorgenommen habe, ist ein "Fade In"-Effekt. Ich m├Âchte ungern, dass Bilder abrupt auf meiner Seite erscheinen. Durch eine kleine CSS-Erweiterung kann das Bild mit einem Fading eingeblendet werden. Die Klasse wird automatisch durch das JavaScript gesetzt, sobald ein Bild geladen und in das src-Attribut gesetzt wird.

.fade-in {
	animation-name: fadeIn;
	animation-duration: 1.3s;
	animation-timing-function: cubic-bezier(0, 0, 0.4, 1);
	animation-fill-mode: forwards;
}

@keyframes fadeIn {
  from {
	opacity: 0;
  }

  to {
	opacity: 1;
  }
}

Dieses CSS-Snippet kannst du in eine geeignete CSS-Datei einf├╝gen, die auf jeder Seite eingebunden wird. Nat├╝rlich kannst du ein wenig mit den Werten spielen, zum Beispiel um eine schnellere Fertigstellung der Animation zu erhalten, indem du die "animation-duration" verringerst.

Wenn du dich gut mit CSS auskennst, kannst du auch deine eigenen Einblend-Animationen erstellen.

Diesen Code findest du noch einmal komplett auf Codepen:

See the Pen Lazy Loading f├╝r Bilder by Dennis Artinger (@Codepalm) on CodePen.

Bitte beachte, dass in Codepen bereits das Update vom 26.08.2019 integriert ist.

 

Performance deiner Webseite mit dem Lazy-Loading testen

Kleiner Tipp: Bevor du das Skript einbindest und die Bilder austauschst, solltest du einen Performance-Test an deiner Seite durchführen. Verwende unseren Performance-Tester, um die Leistung deiner Webseite zu testen. Sichere deine Ergebnisse und führe die Änderungen an deiner Webseite durch. Starte danach eine erneute Analyse und vergleiche die Resultate (Denke an den Server-Cache, falls du einen verwendest).

Ich wusste, dass sich die Ladezeit durch das Skript verbessert, war aber hinterher sehr positiv ├╝berrascht, wie gut das Lazy-Loading tats├Ąchlich ist. Nachdem das Skript implementiert war, habe ich einige Millisekunden Ladezeit gespart! Die Verbesserungen sind nat├╝rlich von Webseite zu Webseite verschieden. Das liegt einerseits an der Gr├Â├če der verwendeten Bildern und andererseits an der Masse der Bilder auf einer Einzelseite.

Update (26.08.2019): Erweiterung des Lazy-Loading-Scripts

In den letzten Tagen habe ich ein wenig mit dem Lazy-Loading-Skript experimentiert. Ich war mit den Ergebnissen auf Google sehr unzufrieden, da die Suchmaschine meine Bilder mit Lazy-Loading nicht erkannte.

Der neue HTML-Tag f├╝r das Lazy-Loading

Da ein transparentes Pixel im Image-Tag enthalten war, hat Google nicht gewartet, bis es ge├Ąndert wurde. Ich vermute, dass die Suchmaschine das transparente Pixel als Bild verwendet und durch die mangelnde Gr├Â├če nicht indexiert hatte.

Um den Fehler zu beheben habe ich das komplette Attribut "src" ersatzlos entfernt.

<img class="lazy-load" data-src="/pfad/zum/bild.jpg" />

Die Erweiterung des Lazy-Loading-Scripts

Da es nun kein transparentes Pixel mehr gab, habe ich in der Entwickler-Konsole von Google Chrome einen Fehler erhalten, wenn der Pfad zu einem Bild nicht korrekt war. Zudem erhielt ich einen "Broken Image"-Fehler, der ein unsch├Ânes Bildchen ausgab.

In meinem Beitrag Broken Image Elemente grafisch optimieren habe ich bereits eine CSS-Formatierung erstellt, die dieses h├Ąssliche Icon durch eine sch├Âne Darstellung verwandeln kann.

Ich musste den Lazy-Loading-Code also so umschreiben, dass bei einem 404-Fehler einer Ressource das src-Attribut mit einem Leerstring oder einer Raute (#) platziert wird. Die Raute wird gerne von Webentwicklern verwendet, um einen fehlerhaften Link oder eine fehlerhafte Ressource darzustellen.

Folgende Funktionen habe ich leicht ge├Ąndert:

/* ... */

function preloadImage( image ) {
	const src = image.dataset.src;
	console.log( image.dataset );
	if( !src )
		return;
	return fetchImage( src ).then(() => { applyImage( image, src ); }, () => { applyImage( image, '#' ); });
}

/* ... */

function applyImage( img, src ) {
	console.log( 'applyImage( '+img+', '+src+' )' );
	if( src != "#" ) {
		img.classList.add( 'js-lazy-image--handled' );
		img.src = src;
		img.classList.add( 'fade-in' );
	}
	else {
		img.src = src;
		img.classList.add( 'js-lazy-image--error' );
	}
}

Durch diese kleine Erweiterung kann nun einen Zustand im Hinterlegen und mit CSS zu pr├╝fen, ob die Ressource fehlerhaft ist.

Die CSS-Erweiterung f├╝r das Lazy-Loading

Zudem habe ich eine kleine Erweiterung in der Broken-Image-Formatierung vorgenommen, um einen Lade-Zustand eines Bilds zu erhalten.

img.lazy-load:not([src]):after {
    content: "Bild '" attr(alt) "' wird geladen";
}

Den Wert der Eigenschaft "content" kannst du nach belieben ├Ąndern. Es ist auch m├Âglich mit FontAwesome ein rotierendes Lade-Symbol zu erstellen.

 

Teile gerne deine Resultate in den Kommentaren mit mir. Ich bin gespannt, ob du auch so gute Verbesserungen feststellen kannst wie andere.

 

Codepalm
Lazy Loading - Bilder nachladen lassen