The technique responsive images prevents users from downloading unnecessary large images. Lazy loading only loads images if they are almost in the visible area, so the user does not download images he does not see. This post shows you how to use lazy loading with responsive images in WordPress.
Update from January 19, 2017: Since WordPress 4.4 you do not need the RICG Responsive Images plugin anymore — the responsive images feature was merged into the core with this release.
Lazy loading and responsive images for WordPress with »lazySizes«
For implementing responsive images into WordPress, there is the RICG Responsive Images plugin. Because the WordPress.org account is listed as one of the developers, the feature will probably be merged into the core (was merged into WordPress 4.4).
We use the script lazySizes for lazy loading the responsive images. To get this working, we have to modify the responsive images markup. The srcset
and sizes
attributes have to be changed into data-srcset
and data-sizes
. Besides that, we need to add the class lazyload
and remove the src
attribute.
To add a fallback for deactivated JavaScript, we generate a noscript
element. Let us begin with the first code part:
Modifying the page and post content
<?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 );
Code language: HTML, XML (xml)
Before displaying the post or page in the frontend, we filter its content with the function lazy_load_responsive_images
. If the post content is empty, we just return $content
without modification. To modify the image attributes to meet our needs, we use the PHP DOM
classes (thanks for the hint to Franz Josef Kaiser).
At first, we create a new DOMDocument()
object. With the method loadHTML()
, we load the HTML code from the $content
variable, which holds the content of the post. To avoid warnings with this method, which seems not to know HTML5 elements yet, the lines above and below the loadHTML()
call are necessary.
Modification of images which were added with installed RICG Responsive Images plugin
Next, we loop through each img
element. Because the responsive images plugin does not modify images which were added before the plugin activation, we have to check if the image is a responsive image. To do that, we check for the attributes sizes
and srcset
. If they are there, we get the content from both with the getAttribute()
method.
After that, we need to set the new attributes data-sizes
and data-srcset
to the img
element with the setAttribute()
method. We use the respective variable with the result of the getAttribute()
call as the second parameter. After doing this, we can remove the sizes
and srcset
attributes and save the image URL for the noscript
image which will be added later. For that, we simply use the value of the src
attribute. If no src
attribute is given (for example this is the case for galleries), we use the data-noscript
attribute.
Modify images without sizes
and srcset
attribute
Images which were added to posts or pages before installing the responsive images plugin neither have a sizes
nor a srcset
attribute. For these images, we fetch the URL like for the other images and add the data-src
attribute.
Adding lazy load class and noscript
element
Finally, we have to add a class to the images to get the lazy loading plugin working and create the noscript
element. First, we save the existing classes of the image in the $classes
variable and append the class lazyload
in the next line. After that, we add the updated class string to the image again. Now we remove the src
attribute (if it still exists).
Next, we create a new noscript
element, which is inserted before the image. Besides that, we need an img
element which gets the classes of the original image (I do not know why it has to be IMG
here, but it does not work with img
). After that, we append the image to the noscript
element and add the src
attribute.
Modifying image attributes of gallery images and post thumbnails
To modify the attributes of gallery images and post thumbnails, WordPress gives us the wp_get_attachment_image_attributes
filter:
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);
Code language: PHP (php)
First, we check if the sizes
attribute is set. If this is the case, we save its content to a variable, remove the attribute and add a data-sizes
attribute. We do the same with the srcset
attribute. Additionally, we add the data-noscript
attribute and remove the src
attribute.
Finally, we add the class lazyload
to existing classes. Now only one thing is missing:
noscript
element for post thumbnails
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);
Code language: PHP (php)
Parts of this code should look familiar to you: We get the classes and URL of the image and create the noscript
element.
Adding the lazy load scripts
Let us insert the lazy loading script and a small style sheet to come to an end.
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 );
Code language: JavaScript (javascript)
That is it. The style sheet just hides the original image, if JavaScript is deactivated. For that, a no-js
class has to be given.
Tip: In the normal case, this should add a noscript
element to all images. But if you are using a theme, which brings its own functions for displaying images, these will not get one. If you know a filter, which allows the modification of the HTML of all images, please leave a comment! 🙂