Responsive images and page reflow fix in WordPress
- 5 Min. Read.
Following our Responsive images and preventing page reflow article, we’ll now work on implementing this responsive images fix in WordPress.
In addition to the responsive images fix, we also want to wrap the post images in a figure tag, and give it a figcaption .
Let’s get coding.
Creating the plugin
We’ll make this a plugin, so first thing we need to do is create a new folder in the wp-content/plugins directory, and call it codecaptain-image-to-figure-converter .
In this folder, create a new file called codecaptain-image-to-figure-converter.php and start with the following template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php /* Plugin Name: CodeCaptain Image to Figure converter Description: Wrap every image in a post in a figure tag & make them responsive, while reserving space on the page Version: 1.0 Author: Sabatino Masala Author URI: https://codecaptain.io/ */ function cc_print_responsive_styles() { } function cc_wrap_images( $content ) { } add_filter( 'the_content', 'cc_wrap_images' ); add_action( 'wp_print_styles', 'cc_print_responsive_styles' ); ?> |
We’ll work with a filter and an action:
- the_content – The “the_content” filter is used to filter the content of the post after it is retrieved from the database and before it is printed to the screen.
- wp_print_styles – The “wp_print_styles” action can be used to output styles in the <head> before the styles are printed. We could also use the wp_enqueue_style function to include the css file, but since there are only a couple of lines of CSS, we’ll save an HTTP request and output it in the head directly.
Manipulating the images
For manipulating our images easily we’ll use an instance of DOMDocument, so start out with loading the content in cc_wrap_images . Each line is documented, take a look:
1 2 3 4 5 6 7 8 |
// Create the document $dom = new DOMDocument(); // Mute error output & load up the wysiwyg content libxml_use_internal_errors(true); // We need utf8 encoding, or we'd get unexpected output $dom->LoadHTML('<?xml encoding="utf-8" ?>' . $content); libxml_clear_errors(); |
The notable line is $dom->LoadHTML('<?xml encoding="utf-8" ?>' . $content); . We need to prepend the content with an encoding declaration, so the string ($content) gets treated as UTF-8, since that document doesn’t already contain such a declaration.
Next up, we’ll load all images and start to loop over them.
1 2 3 4 5 6 7 8 |
// Fetch all the images $images = $dom->getElementsByTagName('img'); foreach ($images as $image) { // ... } |
In the loop, we’ll get the src, alt, class, width & height attribute of the image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Get the image src $src = $image->getAttribute('src'); // Get the image alt $alt = $image->getAttribute('alt'); // If no alt is set, we have our default if (!$alt || $alt == '') { $alt = '© CodeCaptain'; } // Get all the image classes $classes = $image->getAttribute('class'); // Store width & height in a variable $width = $image->getAttribute('width'); $height = $image->getAttribute('height'); // Division by 0 protection if (!$width) { $width = 1; } |
Calculate the aspect, rounded to 2 decimals.
1 2 |
// We want to calculate the aspect ratio of the image (2 decimals) $aspect = round($height / $width * 10000) / 100; |
Creating the new DOM structure
We want to achieve a new DOM structure, which looks like this.
1 2 3 4 5 6 7 8 9 10 |
<figure> <div class="wp-aligner"> <div class="image-wrapper"> <div class="cc-responsive-container"> <img src="" alt=""> </div> </div> </div> <figcaption></figcaption> </figure> |
So after you calculated the aspect, let’s create some elements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// Create the figure tag $figure = $dom->createElement('figure'); // Create the responsive container $responsiveContainer = $dom->createElement('div'); $responsiveContainer->setAttribute('class', 'cc-responsive-container'); // Create the wrapper $imageWrapper = $dom->createElement('div'); $imageWrapper->setAttribute('class', 'image-wrapper'); // Create the alignment div $wpAlignDiv = $dom->createElement('div'); $wpAlignDiv->setAttribute('class', 'wp-aligner ' . $classes); // Set the padding-bottom to the aspect as a percentage $responsiveContainer->setAttribute('style', 'padding-bottom: ' . $aspect . '%'); // We don't want to stretch the image, so we'll set the max-width and max-height to $width and $height respectively $imageWrapper->setAttribute('style', 'max-width: ' . $width . 'px; max-height: ' . $height . 'px;'); // Create the figcaption $figCaption = $dom->createElement('figcaption'); // Set the value of the figcaption to the alt tag of the image $figCaption->nodeValue = $alt; |
Since the image will be positioned absolute, the alignment classes which WordPress adds will no longer work (alignleft, alignright, aligncenter), that’s why we create a wrapping div with a class ‘wp-aligner’, to which we append all the image classes, so we can easily align the images later.
The cc-responsive-container will have a style attribute, where we set the calculated padding-bottom inline.
The image-wrapper will also have a style attribute with a max-width and max-height, so we don’t make the image larger than it originally is.
And finally, we create a figcaption and set the alt attribute of the image as the content.
After creating the new nodes, we have to add it to the DOM structure.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// Add the figcaption and aligndiv to the figure $figure->appendChild($wpAlignDiv); $figure->appendChild($figCaption); // Add the imagewrapper to the aligndiv $wpAlignDiv->appendChild($imageWrapper); // Add the responsive container to the imagewrapper $imageWrapper->appendChild($responsiveContainer); // Finally we need to replace the image with our new figure $image->parentNode->replaceChild($figure, $image); // And after we replaced the image, we want to re-add it to the responsive container $responsiveContainer->appendChild($image); // We don't need the classes on the image anymore $image->removeAttribute('class'); |
This was the last piece of code needed in the loop.
There’s a small issue we have to fix: an image can be a child of a <p> tag, but a figure not, so we have to loop over all figures and check if its direct parent is a paragraph tag, and if it is, we have to remove it.
Put this code after the loop.
1 2 3 4 5 6 7 8 9 10 11 12 |
// Let's loop over all newly created figures $figures = $dom->getElementsByTagName('figure'); // If the figure is wrapped in a p-tag, we'll remove the p-tag foreach ($figures as $figure) { if ($figure->parentNode->tagName == 'p') { $parent = $figure->parentNode; $parent->parentNode->replaceChild($figure, $parent); } } |
And finally, we have to return our new content.
1 2 |
// Last step, return our new content return $dom->saveHTML(); |
Creating the base css
Create a new file called responsive-styles.css and put the following CSS in it.
1 2 3 4 5 6 7 8 9 10 11 12 |
.cc-responsive-container { position: relative; overflow: hidden; height: 0; } .cc-responsive-container img { width: 100%; height: 100%; position: absolute; top: 0; left: 0; } |
Move back to codecaptain-image-to-figure-converter.php and in cc_print_responsive_styles() we will output the css styles in the <head> .
1 2 3 4 5 6 7 |
function cc_print_responsive_styles() { ?> <style> <?php include 'responsive-styles.css'; ?> </style> <? } |
And that concludes our work, activate the plugin in your WordPress dashboard and all images in your posts will be replaced by our new structure. The responsive images will also reserve their space in the page, so the page won’t reflow when the images load.
In conclusion
Thanks for reading & hope this helped some of you out.