I have a WordPress blog and am trying to implement the foresight.js image script. In short, I need to target all post images, swap out the src, width, & height
attributes with data-src, data-width, & data-height
attributes. I then need to duplicate the image line and wrap it in <noscript>
tags. This is the structure I’m trying to have WordPress generate/create:
<img data-src="wordpress/image/url/pic.jpg" data-width="{get width of image with PHP & pass-in that value here} data-height="{get height of image with PHP and pass-in that value here}" class="fs-img">
<noscript>
<img src="wordpress/image/url/pic.jpg">
</noscript>
I have searched the WordPress codex and the best possible route I can find are to use filters (ie. ‘get_image_tag’ & ‘image_tag’) for modifying the html that WordPress outputs for each image. I’m thinking that one of these options should work, or that I can do some pattern matching with regex (I know, not ideal), throw in a preg_replace
and then inject this back into the_content
filter.
I have tried some of these options but cannot get any to work. Could someone please offer some help? Found one suggestion here, but can’t even get it to work!
‘get_image_tag’ attempt:
Found this particular one on the web, but it would need modified to fit my logic (see above structure)…can’t make sense of what the preg_replace
array is doing on my own.
<?php function image_tag($html, $id, $alt, $title) {
return preg_replace(array(
'/'.str_replace('//','//',get_bloginfo('url')).'/i',
'/s+width="d+"/i',
'/s+height="d+"/i',
'/alt=""/i'
),
array(
'',
'',
'',
alt='"' . $title . '"'
),
$html);
}
add_filter('get_image_tag', 'image_tag', 0, 4);
?>
Another ‘get_image_tag’ attempt:
<?php function get_image_tag($id, $alt, $title, $align, $size='full') {
list($width, $height, $type, $attr) = getimagesize($img_src);
$hwstring = image_hwstring($width, $height);
$class = 'align' . esc_attr($align) . ' size-' . esc_attr($size) . ' wp-image-' . $id;
$class = apply_filters('get_image_tag_class', $class, $id, $align, $size);
$html = '<img src="' . esc_attr($img_src) . '" alt="' . esc_attr($alt) . '" title="' . esc_attr($title).'" data-width="' . $width . '" data-height="' . $height . '" class="' . $class . ' fs-img" />';
$html = apply_filters( 'get_image_tag', $html, $id, $alt, $title, $align, $size);
return $html;
}
?>
Pattern-matching attempt:
Tried creating my own regex on this one, but not sure if it’s correct.
<?php function restructure_imgs($content) {
global $post;
$pattern = "/<img(.*?)src=('|")(.*?).(bmp|gif|jpeg|jpg|png)(|")(.*?)>/i";
list($width, $height, $type, $attr) = getimagesize($2$3.$4$5);
$hwstring = image_hwstring($width, $height);
$replacement = '<img$1data-src=$2$3.$4$5 title="'.$post->post_title.'" data-width="'.$width.'" data-height="'.$height.'" class="fs-img"$6>';
$content = preg_replace($pattern, $replacement, $content);
return $content;
}
add_filter('the_content', 'restructure_imgs');
?>
Unfortunately can’t get any of these examples to work. Any help or sharing your pre-written scripts/functions would be much appreciated! Thanks for helping a student learn!!
The filters you are trying to use run on image insertion, so it is not possible to swap all the images already present in your posts using these filters. It should work, however, if you intend to change to
img
tags from now on.The filter
the_content
, however, is applied to the post after it is retrieved from the database and before displaying it to screen. I believe that, in order to make a change to your existing posts without reinserting the images, you could use this filter.You can parse
the_content
using the PHP DOMDocument class. When it comes to HTML parsing in PHP, do not use regex.I wrote a sample function for what you want to do, it’s a bit verbose in order to explain the passages. Feel free to tweak it at will.
I didn’t test it specifically with WordPress, but I tested it with a sample post output and it should work.
This code works very well for me, but I had some issues during the process to get the final version.
Warning
The first is that the server started showing some warnings like
Warning: DOMDocument::loadHTML(): Unexpected end tag
. This question shows more details of the error and how to solve it, but to add, adding thislibxml_use_internal_errors(true);
code at main function before callingloadHTML
fix the problem.The second (group of) problem was with the method DOMDocument::loadHTML.
Character encoding
The documentation has a very important comment from Shane Harte about character encoding of UTF-8 documents. So, before
loadHTML
I had to use[mb_convert_encoding][4]
with'HTML-ENTITIES', "UTF-8"
parameters.HTML wrapper
The second problem with the method was that the output always contained a doctype + HTML + body tags and in this case, this is a huge problem because you are dealing with only one clipping of the document (
the_content()
) and not the whole.The simplest way to fix this is to use libxml constants as parameters of the loadHTML method
Something like
$output->loadHTML(mb_convert_encoding($the_content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
Unexpected end first tag of
the_content()
Another problem I started to see was that the tag of the first element of “the_content” was not closed properly.
For example, if the first element was a
<p>
, the entire content ofthe_content
was wrapped by that first<p>
. In many cases, the content started with an H2, then it had the same issue.After a lot of research, I found this comment of Nicholas Shanks that opened my mind:
So, the first part of my code looks like this:
âï¸