There are multiple ways to change the markup of your images, <img>
, in WordPress. This article will show how we changed the <img>
markup of WordPress posts so they can be lazy loaded. There are at least three (3) filters that can be used:
- ‘the_content’ filter to alter images in the post_content as they are placed on the page
- ‘wp_get_attachment_image_attributes’ filter to alter when using
wp_get_attachment_image()
in php - ‘get_image_tag_class’ and ‘get_image_tag’ filters to alter when an image is placed in the post_content after selecting it from media library
We will be showing how we changed the image markup from
<img src="https://jhtechservices.com/wp-content/uploads/logo.png" alt="" width="644" height="225" class="aligncenter size-full wp-image-1137" srcset="https://jhtechservices.com/wp-content/uploads/logo.png?w=644&ssl=1 644w, https://jhtechservices.com/wp-content/uploads/logo.png?resize=300%2C105&ssl=1 300w" sizes="(max-width: 644px) 100vw, 644px">
to
<img src=""
data-src="https://jhtechservices.com/wp-content/uploads/logo.png?resize=644%2C225&ssl=1" alt=""
class="aligncenter size-full wp-image-1137 lazyload"
data-srcset="https://jhtechservices.com/wp-content/uploads/logo.png?w=644&ssl=1 644w, https://jhtechservices.com/wp-content/uploads/logo.png?resize=300%2C105&ssl=1 300w"
sizes="(max-width: 644px) 100vw, 644px" width="644" height="225">
But the code shown in this article can easily be altered for other changes.
Filter 1: ‘wp_get_attachment_image_attributes’ filter to alter when using wp_get_attachment_image()
in php
This is a great way to change the <img>
markup for when your code uses the wp_get_attachment_image()
to generate a responsive <img>
tag. You can hook into wp_get_attachment_image_attributes
filter where your function receives an array of attributes with their values. Here is how we add all the lazy load alterations:
add_filter( 'wp_get_attachment_image_attributes', 'gs_change_attachment_image_markup' );
/**
* Change src and srcset to data-src and data-srcset, and add class 'lazyload'
* @param $attributes
* @return mixed
*/
function gs_change_attachment_image_markup($attributes){
if (isset($attributes['src'])) {
$attributes['data-src'] = $attributes['src'];
$attributes['src'] = ''; //could add default small image or a base64 encoded small image here
}
if (isset($attributes['srcset'])) {
$attributes['data-srcset'] = $attributes['srcset'];
$attributes['srcset'] = '';
}
$attributes['class'] .= ' lazyload';
return $attributes;
}
This works but it will not change the images that have been added to the content of posts. We will have to look at the filter, the_content
, to do that.
Filter 2: ‘the_content’ filter to alter images in the post_content as they are placed on the page
So the above code provides a solution for any images we add programmatically, but not for the images added to the post_content. If you look at the content of a post in the dashboard using the ‘Text’ tab instead of the ‘Visual’ tab, you will see the image markup and that it does not contain the srcset
or sizes
attributes. If you look at the same <img>
source after the post is rendered in the browser, these attributes will have been added (if you are running WordPress 4.4+). After some digging we found this was done by the function ‘wp_make_content_images_responsive()’ that is hooked into the_content
filter. Perusing this WordPress function, we found no filters or hooks we could use to alter the image markup so we determined we must create our own function to search through the content and change the <img>
tags. Luckily, we found a great function by Sunyatasattva on StackExchange that we modified to do exactly what we needed.
add_filter('the_content', 'gs_add_img_lazy_markup', 15); // hook into filter and use
priority 15 to make sure it is run after the srcset and sizes attributes have been added.
/**
*
* Modified from: Sunyatasattva
* https://wordpress.stackexchange.com/questions/81522/change-html-structure-of-all-img-tags-in-wordpress
* @param $the_content
*
* @return string
*
* Initial use of code gave warning: DOMDocument::loadHTML(): Unexpected end tag : p
* Due to invalid HTML
*
* https://stackoverflow.com/questions/11819603/dom-loadhtml-doesnt-work-properly-on-a-server
*
* libxml_use_internal_errors(true);
*/
function gs_add_img_lazy_markup($the_content) {
libxml_use_internal_errors(true);
$post = new DOMDocument();
$post->loadHTML($the_content);
$imgs = $post->getElementsByTagName('img');
// Iterate each img tag
foreach( $imgs as $img ) {
if( $img->hasAttribute('data-src') ) continue;
if( $img->parentNode->tagName == 'noscript' ) continue;
$clone = $img->cloneNode();
$src = $img->getAttribute('src');
$img->removeAttribute('src');
$img->setAttribute('data-src', $src);
$srcset = $img->getAttribute('srcset');
$img->removeAttribute('srcset');
if( ! empty($srcset)) {
$img->setAttribute('data-srcset', $srcset);
}
$imgClass = $img->getAttribute('class');
$img->setAttribute('class', $imgClass . ' lazyload');
$no_script = $post->createElement('noscript');
$no_script->appendChild($clone);
$img->parentNode->insertBefore($no_script, $img);
};
return $post->saveHTML();
}
Filter 3: ‘get_image_tag_class’ and ‘get_image_tag’ filters to alter when an image is placed in the post_content after selecting it from media library
Lasty, we have a couple of filters that allow us to alter the image tag early on. For our lazy load solution, we could not alter the tags so early. If we changed the src
attribute to data-src
right after adding an image, users in the ‘Visual’ tab of the post editor would not be able to see the image. The only part of this solution we could have done early is add the class lazyload
.
function image_tag_class($class, $id, $align, $size) {
return $class . ' lazyload';
}
add_filter('get_image_tag_class', 'image_tag_class', 10, 4);
Now lazyload
is added to our <img>
tag as soon as we add it to our post.
We did not use this solution since we were already adding the class using the_content
filter solution above.
The last filter, get_image_tag
, can be used to alter all parts of the <img>
tag before it is placed in the content. This filter gives you the full HTML of the complete tag, and the attributes: id, alt, and title. You can check out the code from an article on sitepoint by Craig Buckler for an example of using preg_replace
to return a modified image tag.
We hope this helps someone out there. Leave us comment!
How did you handle noscript for wp_get_attachment_image() and are these filters covering all image use cases of WordPress?
I’m trying to implement “lazySizes” so that any image inserted will be lazy loaded.
No, it won’t cover all use cases of WordPress. Theme images like header and footer images, widget images, or sidebar images may not be covered by this.
You should be able to implement lazySizes using the Filter 2 section of this blog for all images in the content of the page or post.
If you are developing the theme and adding images using php, you can use the Filter 1 section of this blog to handle those images.
Filter 2 seems to break the_content for me, all non-latin characters becames mess.
Sounds like an issue with DOMDocument and encoding. Look at the ‘Note’ at the bottom of this page (http://php.net/manual/en/class.domdocument.php) where it talks about encoding. “Use utf8_encode()
and utf8_decode() to work with texts in ISO-8859-1”. Maybe that is related to what you are experiencing.