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