You can use DOMDocument to parse the HTML content like so:
add_filter( 'the_content', 'add_html_after_first_image' );
function add_html_after_first_image( $content ) {
$my_custom_html = '<div>MY CUSTOM CODE</div>';
// Create a dom document from the post content.
// The content needs to be wrapped in a root element to be valid xml, hence the div tags.
// I used loadXML because loadHTML adds the unnecessary <DOCTYPE>, <html>, and <body>.
if( $dom = DOMDocument :: loadXML( '<div>' . $content . '</div>' ) ) {
// Create a document fragment with your custom html.
$custom = $dom -> createDocumentFragment();
$custom -> appendXML( $my_custom_html );
// Get the root element, and first child.
$root = $dom -> firstChild;
$first = $root -> firstChild;
// Check if the first child is a paragraph, the first grandchild is an image,
// and if the paragraph has no content other than the image.
$has_image = 'p' == $first -> tagName
&& '' == trim( $first -> textContent )
&& 1 == count( $first -> childNodes )
&& 'img' == $first -> firstChild -> tagName;
// If $has_image is true then add the custom div after the first paragraph,
// otherwise add it before.
$root -> insertBefore( $custom, $has_image ? $first -> nextSibling : $first );
// Set the new content to the altered html.
$content = $dom -> saveHTML();
}
return $content;
}
EDIT:
Here is an updated function that has been optimized to run more quickly than the function above.
Tip: The <p> tag must not contain any content other than the <img /> tag. (That includes whitespace)
add_filter( 'the_content', 'add_html_after_first_image' );
function add_html_after_first_image( $content ) {
$my_custom_html = '<div>MY CUSTOM CODE</div>';
// Explode the content to extract and parse only the first paragraph.
$parts = explode( '</p>', $content, 2 );
$p = $parts[0] . '</p>';
// Create a dom document from the first paragraph of content.
// I used loadXML because loadHTML adds the unnecessary <DOCTYPE>, <html>, and <body>.
// Checking for an opening <p> tag prevents the creation of a DOMDocument,
// and lowers the execution time on posts that don't start with a paragraph.
if( substr( $p, 0, 3 ) == '<p>' && $dom = @DOMDocument :: loadXML( $p ) ) {
// Create a document fragment with your custom html.
$custom = $dom -> createDocumentFragment();
$custom -> appendXML( $my_custom_html );
$first = $dom -> firstChild;
// Check if the first child is a paragraph, the first grandchild is an image,
// and if the paragraph has no content other than the image.
$has_image = 'p' == $first -> tagName
&& '' == $first -> textContent
&& 1 == count( $first -> childNodes )
&& 'img' == $first -> firstChild -> tagName;
// If $has_image is true then add the custom div after the first paragraph,
// otherwise add it before.
$dom -> insertBefore( $custom, $has_image ? $first -> nextSibling : $first );
// Set the new content to the altered html.
$p = $dom -> saveHTML();
}
// If the dom document could not be created, then the first element is not a
// paragraph and the custom code should be prepended to the content.
else $p = $my_custom_html . $p;
// Append the rest of the content to the paragraph.
return $p . $parts[1];
}
<div class="entry-content">
in the post editor? Or is applied by your template?p
tag has attributes (style
,class
etc.)?style
,class
orid
attributes with thep
tag, but your question reminds me that I may have to. How thoughtful. :) Every tut on the web, unfortunately, also seems to use regex :S