Responsive Images und Lazy Loading in WordPress

Die Zeiten ändern sich.

Dieser Beitrag scheint älter als 2 Jahre zu sein – eine lange Zeit im Internet. Der Inhalt ist vielleicht veraltet.

Um Besuchern von Websites keine unnötig großen Bilder zumuten zu müssen, kann die Technologie Responsive Images genutzt werden. Um die Bilder erst zu laden, wenn sie sich fast im sichtbaren Bereich befinden, gibt es das Lazy Loading. Wie ihr die beiden zusammen für eure WordPress-Seite einsetzen könnt, zeige ich euch hier.

Update vom 19. Januar 2017: Seit WordPress 4.4 wird das Plugin RICG Responsive Images nicht mehr benötigt, da die Funktion für responsive Images in den Core integriert wurden.

Lazy Loading und Responsive Images für WordPress mit »lazySizes«

Für Responsive Images gibt es ein WordPress-Plugin namens RICG Responsive Images, das ich bereits kurz vorgestellt habe. Da unter anderem der WordPress.org-Account an der Entwicklung beteiligt ist, wird es so oder so ähnlich vermutlich mal den Weg in den Core finden (hat es in WordPress 4.4).

Für das Lazy Loading der Responsive Images nutzen wir das JavaScript lazySizes. Das Markup, das wir von dem Plugin „RICG Responsive Images“ bekommen, muss dafür modifiziert werden. Die srcset- und sizes-Attribute müssen in die Attribute data-srcset und data-sizes geändert werden. Des Weiteren muss eine Klasse lazyload hinzugefügt und das src-Attribut entfernt werden.

Um einen Fallback für Browser zu erstellen, die kein JavaScript beherrschen, fügen wir bei jedem Bild noch ein noscript-Element ein. Fangen wir mit dem ersten Code-Teil an:

Anpassen des Seiten- und Artikelinhalts

<?php
defined( 'ABSPATH' ) or die( "Nothing to see!" );
/**
 * Plugin Name: Lazy Load Responsive Images
 */

function lazy_load_responsive_images ( $content ) {
   if ( empty( $content ) ) {
      return $content;
   }
   $dom = new DOMDocument();
   libxml_use_internal_errors( true );
   $dom->loadHTML( $content );
   libxml_clear_errors();
   foreach ( $dom->getElementsByTagName( 'img' ) as $img ) {
      if ( $img->hasAttribute( 'sizes' ) && $img->hasAttribute( 'srcset' ) ) {
         $sizes_attr = $img->getAttribute( 'sizes' );
         $srcset     = $img->getAttribute( 'srcset' );
         $img->setAttribute( 'data-sizes', $sizes_attr );
         $img->setAttribute( 'data-srcset', $srcset );
         $img->removeAttribute( 'sizes' );
         $img->removeAttribute( 'srcset' );
         $src = $img->getAttribute( 'src' );
         if ( ! $src ) {
            $src = $img->getAttribute( 'data-noscript' );
         }
      } else {
         $src = $img->getAttribute( 'src' );
         if ( ! $src ) {
            $src = $img->getAttribute( 'data-noscript' );
         }
         $img->setAttribute( 'data-src', $src );
      }
      $classes = $img->getAttribute( 'class' );
      $classes .= " lazyload";
      $img->setAttribute( 'class', $classes );
      $img->removeAttribute( 'src' );
      $noscript      = $dom->createElement( 'noscript' );
      $noscript_node = $img->parentNode->insertBefore( $noscript, $img );
      $noscript_img  = $dom->createElement( 'IMG' );
      $noscript_img->setAttribute( 'class', $classes );
      $new_img = $noscript_node->appendChild( $noscript_img );
      $new_img->setAttribute( 'src', $src );
      $content = $dom->saveHTML();
   }

   return $content;
}
add_filter( 'the_content', 'lazy_load_responsive_images', 20 );

Vor der Ausgabe im Frontend durchläuft der Inhalt eines Beitrags oder einer Seite die Funktion lazy_load_responsive_images. Wenn der Inhalt leer ist, wird einfach $content wieder zurückgegeben. Um die Attribute der Bilder so anzupassen, wie wir das benötigen, nutzen wir die DOM-Klassen von PHP, die schneller und präziser als reguläre Ausdrücke arbeiten (Danke an dieser Stelle für den nützlichen Hinweis an Franz Josef Kaiser).

Zuerst erzeugen wir ein neues Exemplar der Klasse DOMDocument(). Mit der Methode loadHTML() laden wir den HTML-Code aus der Variable $content, die unseren Inhalt bereitstellt. Um Warnungen bei der Methode loadHTML() zu umgehen, die scheinbar noch keine HTML5-Elemente kennt, sind die Zeile über und unter dem Aufruf der loadHTML()-Methode zuständig.

Anpassungen für Bilder, die mit installiertem „RICG Responsive Images“-Plugin eingefügt wurden

Als nächstes durchlaufen wir für jedes img-Element eine Schleife. Da das Responsive-Images-Plugin keine bereits vor der Installation eingefügten Bilder aktualisiert, müssen wir prüfen, ob es sich um ein Responsive Image handelt oder nicht. Dafür prüfen wir auf die Attribute sizes und srcset. Sind die vorhanden, holen wir uns mit der getAttribute()-Methode den Inhalt des sizes- und srcset-Attributs.

Danach müssen wir dem img-Element mittels der setAttribute()-Methode die neuen Attribute data-sizes sowie data-srcset übergeben, mit denen das Lazy-Load-Plugin arbeitet. Als zweiten Parameter übergeben wir die Variablen, die das jeweilige Ergebnis des getAttribute()-Methodenaufrufs beinhalten. Danach entfernen wir die Attribute sizes und srcset und speichern für das Bild in dem noscript-Element den Wert des src-Attributs. Falls kein src-Attribut vorhanden ist (unter anderem bei Galerien der Fall, da bei denen das src-Attribut bereits in einer anderen Funktion entfernt wird), nutzen wir das data-noscript-Attribut.

Anpassungen für Bilder ohne srcset- und sizes-Attribut

Bei Bildern, die vor der Installation des Plugins für responsive Images eingefügt wurden, gibt es natürlich weder sizes- noch srcset-Attribut. Bei diesen Bildern holen wir uns wie bei den anderen Bildern den Link zu dem Bild und fügen dann das Attribut data-src hinzu.

Lazy-Load-Klasse und noscript-Element hinzufügen

Abschließend müssen wir den Bildern noch eine Klasse mitgeben, die das Lazy-Load-Plugin vorgibt, und das noscript-Element erstellen. Wir speichern zuerst die Klassen des Bildes in die Variable $classes und hängen in der nächsten Zeile der Zeichenkette ein lazyload an. Danach fügen wir dem Bild den aktualisierten Klassen-String hinzu. Nun wird das eventuell noch vorhandene src-Attribut entfernt.

Abschließend erstellen wir ein neues noscript-Element, das wir vor dem Bild einfügen. Außerdem brauchen wir noch ein img-Element, dem wir die Klassen des Originalbildes mitgeben (ich weiß nicht genau, warum es hier IMG heißen muss, bei img lädt sich das Plugin aber tot). Danach wird das Bild in das noscript-Element eingefügt und das src-Attribut eingefügt.

Anpassen der Bild-Attribute von Galeriebildern und Featured Images

Für Bilder aus Galerien und Featured Images gibt es den Filter wp_get_attachment_image_attributes, an den wir unsere Funktion übergeben werden:

function lazy_load_responsive_images_modify_post_thumbnail_attr( $attr, $attachment, $size ) {
   if ( isset( $attr['sizes'] ) ) {
      $data_sizes = $attr['sizes'];
      unset( $attr['sizes'] );
      $attr['data-sizes'] = $data_sizes;
   }

   if ( isset( $attr['srcset'] ) ) {
      $data_srcset = $attr['srcset'];
      unset( $attr['srcset'] );
      $attr['data-srcset']   = $data_srcset;
      $attr['data-noscript'] = $attr['src'];
      unset( $attr['src'] );
   }

   $attr['class'] .= ' lazyload';

   return $attr;
}
add_filter( 'wp_get_attachment_image_attributes', 'lazy_load_responsive_images_modify_post_thumbnail_attr', 20, 3);

Wir prüfen zuerst, ob das Attribut sizes vorhanden ist. Ist das der Fall, speichern wir dessen Inhalt in einer Variable, löschen das Attribut und fügen dem Bild das neue Attribut data-sizes hinzu. Gleiches tun wir mit dem srcset-Attribut. Hier fügen wir noch das data-noscript-Attribut ein und löschen das src-Attribut.

Abschließend fügen wir die Klasse lazyload den vorhandenen Klassen hinzu. Nun fehlt noch das Hinzufügen des noscript-Elements für die Featured Images.

noscript-Element für Featured Images

function lazy_load_responsive_images_filter_post_thumbnail_html( $html, $post_id, $post_thumbnail_id, $size, $attr ) {
   $dom = new DOMDocument();
   libxml_use_internal_errors( true );
   $dom->loadHTML( $html );
   libxml_clear_errors();
   foreach ( $dom->getElementsByTagName( 'img' ) as $img ) {
      $src     = $img->getAttribute( 'data-noscript' );
      $classes = $img->getAttribute( 'class' );
   }
   $noscript_element = "<noscript><img src='" . $src . "' class='" . $classes . "'></noscript>";
   $html .= $noscript_element;

   return $html;
}
add_filter( 'post_thumbnail_html', 'lazy_load_responsive_images_filter_post_thumbnail_html', 10, 5);

Vieles dürfte euch hier schon bekannt vorkommen: Wir holen uns die Klassen und den Link zu dem Bild wieder mit den Dom-Klassen. Danach bauen wir das noscript-Element zusammen und hängen es einfach an das HTML des Featured Images.

Einfügen des Lazy-Load-Skripts

Nun müssen wir nur noch das Skript für das Lazy Loading einbauen und ein kleines Stylesheet einfügen.

function lazy_load_responsive_images_script() {
   wp_enqueue_script( 'lazy_load_responsive_images_script-lazysizes', plugins_url() . '/lazy-load-responsive-images/js/lazysizes.js', '', false, false );
   wp_enqueue_style( 'lazy_load_responsive_images_style', plugins_url() . '/lazy-load-responsive-images/css/lazy_load_responsive_images.css' );
}
add_action( 'wp_enqueue_scripts', 'lazy_load_responsive_images_script', 20, 0 );

Das wär es auch schon. Das Stylesheet kümmert sich lediglich um das Verstecken des Originalbildes, wenn kein JavaScript aktiviert ist. Dafür muss eine no-js-Klasse vorhanden sein!

Hinweis: Im Normalfall sollten so alle Bilder auch ein noscript-Element bekommen. Wenn ihr aber ein Theme einsetzt, das mit eigenen Funktionen Bilder anzeigt (zum Beispiel die ersten Bilder aus einer Galerie), dann werden diese keins bekommen. Wenn ihr einen Filter kennt, mit dem das HTML aller Bilder angepasst werden kann, immer her damit 🙂

Marc hat das Plugin in einer leicht abgewandelten Form bei WordPress.org hochgeladen.

Das könnte auch interessant sein

12 Kommentare zu »Responsive Images und Lazy Loading in WordPress«

  1. Marc

    Hi Flo,

    hast Du das Script irgendwo auf github oder wordpress.org? Sonst würde ich es mit ein paar Anpassungen mal dort hochladen und auch auf WordPress.org laden. Natürlich mit deinem Namen als Referenz.

    LG

    Marc

    Antworten
      1. Marc

        Hi Flo,

        soll ich das mal erledigen und auf wp.org stellen? Ich habe ein paar Dinge ändern müssen (UTF-8).

        Du hast in dem Script leider keine Lizenz wie GPL v2 angegeben. Ich kann das für die Allgemeinheit und zur besseren Pflege des Codes gerne auf github und wordpress.org stellen. Dafür brauche ich aber deine explizite Erlaubnis. =)

        Liebe Grüße und schönen Sonntag noch.

        - Marc

        Antworten
  2. Akhil

    Hi Florian,

    Thanks for this great plugin! Just started using it on a project. I made a slight modification to include srcset and sizes on the noscript version so that any modern browsers with javascript disabled will get responsive images too. Any particular reason you didn't add it?

    Warm Regards,
    Akhil.

    Antworten
    1. Florian

      Hi Akhil,

      no specific reason, but at the time of writing the code no browser (I think) supported responsive images without a JavaScript polyfill 🙂

      Greetings!
      Florian

      Antworten
    1. Akhil

      Hi Marc,

      WordPress is not adding srcset for img inside noscript by default. Assigning the data-srcset and data-sizes values gets it working though 🙂

      Antworten
  3. Timothy T

    Hi Florian,

    Thanks for this! But why is the image inside an iframe loaded through the site is not loading? Thanks,.

    Antworten
    1. Florian

      Hi Timothy,

      just wanted to guide you to the plugin support forum but noticed you already found it 🙂

      I only provided the initial code — Marc is maintaining the plugin.

      Cheers,
      Florian

      Antworten

Schreibe einen Kommentar zu i-love-webdesign Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.