When adding an image to a page or post, Wordpress is automatically adding a paragraph tag <p> as parent holding the image. Like so:

<p><img src="my-image.jpg" alt=""></p>

Since there is no parent selector in CSS I'd love to find a solution to apply a specific classname to those paragraphs holding an actual image. The example above would thus result in:

<p class="my-class"><img src="my-image.jpg" alt=""></p>

Any idea if I can use add_filter() to apply a classname to each p that holds an image?

4 Answers 4


You could use jQuery if you don't mind to rely on JavaScript for adding the class.

$(document).ready(function() {

Update: the .has() method is probably faster, see this jsperf.com test.

$(document).ready(function() {

If you need this to be a PHP solution. You could do something like this using 'the_content' filter. This will add the class name "content-img-wrap" to any paragraph that wraps only an image tag when Wordpress prints your post content. So this will not wrap a paragraph with an image and a span. See update if that is what your looking for.

add_filter( 'the_content', 'img_p_class_content_filter' ,20);
function img_p_class_content_filter($content) {
    // assuming you have created a page/post entitled 'debug'
    $content = preg_replace("/(<p)(>[^<]*<img[^>]+>[^<]*)(<\/p>)/i", "\$1 class='content-img-wrap'\$2\$3", $content);

    return $content;

** UPDATE BELOW: In response to the need for a more flexible wrap. I created and quickly tested this regex against 3 automatically wrapped paragraphs (meaning I didn't add the p tags, WP did) with images at the end of a post. They each also contained some random tags and carriage returns for testing likely cases and flexibility. The idea is that you don't have to match anything after the <img> because the presence of any <img> proves our case. Then we just add our custom class/es in (improvements more then welcome):

function img_p_class_content_filter($content) {
    // assuming you have created a page/post entitled 'debug'
    $content = preg_replace("/(<p[^>]*)(\>.*)(\<img.*)(<\/p>)/im", "\$1 class='content-img-wrap'\$2\$3\$4", $content);

    return $content;
  • From taking a quick look at the regex, I think [^<]* is too narrow, as it would not consider span's occurring inside paragraphs. Commented Feb 8, 2015 at 14:21
  • @BramVanroy, I didn't even guessed that you can put a bounty on a question which is not your own. IMO you should ask a question as this one is resolved and I will be surprised if many people will bother reading it. Commented Feb 8, 2015 at 18:07
  • 6
    @MarkKaplun So you think I'd rather had written the exact same post again? It would've been marked as a duplicate I'm afraid. Commented Feb 8, 2015 at 18:24
  • @BramVanroy, yes it is awkward, but I don't know if you will be able to award the bounty at all and as I said most people probably do not look at resolved questions. Commented Feb 9, 2015 at 6:51
  • @MarkKaplun I+ can assign the bounty, not a problem. Also I think that a lot of people looking to get bounties look at bthe bounty category, so they'll find their way here. Commented Feb 9, 2015 at 14:00

While I understand wanting the front end to be as lean as possible and therefore stating you want a PHP solution...

Personally, I spent quite a bit of time on this just for fun, and I kept coming up with ways to break the regex matching (hence, why not to use PHP without a library add-on specifically designed to traverse HTML, as mentioned in the link above)...

For example:

This screenshot's rule will match <p> or even <P > (the simplest match, not trying yet for P tags that already have a class or have other attributes but not a class), but see that it has to be matched WITHIN the entire block of closing tags (where PHP regex for HTML falls apart at the seams)... the arrow points to where a closing P tag and opening P tag are on the same line and therefore if you were to count this as a FIND and then replace the <p with <p class="my-class", you'd be adding my-class to the wrong paragraph.

If you didn't want such an "every scenario" use case, it's doable via regex. If you narrow your scope a bit.

If you decide to limit your scope, you might be interested in this information:

    From wp-includes/default-filters.php
    these run on the_content with default priority (10):
    shortcode renders may add new paragraphs and/or new images
        do_shortcode runs on the_content priority 11 so we hook in afterwards so $content already has shortcodes rendered
add_filter('the_content', 'se_16033', 15); //a priority higher than 11

Here's some starting code if you do force yourself down this complicated route (instead of via jQuery):

    function se_16033($content) {
    if( !is_singular() || !is_main_query() //https://pippinsplugins.com/playing-nice-with-the-content-filter/
    ) {
        return $content;

    $content = force_balance_tags($content); //We do not want to be REGEXing on unbalanced HTML tags so we must make sure they are balanced before we get started -- garbage in, worse garbage out -- http://codex.wordpress.org/Data_Validation#HTML

    $patterns = array();
    $patterns[0] = '/quick/'; //first match p tags with existing classes because we just want to add an additional class
    $patterns[1] = '/brown/'; //second, match p tags with space (e.g. style) but without class=""
    $patterns[2] = '/fox/'; //third, match <p> tags without attributes

    $replacements = array();
    $replacements[0] = 'slow';
    $replacements[1] = 'black';
    $replacements[2] = 'bear';

    preg_replace_all($patterns, $replacements, $content);

    return $content;


Note the use of http://codex.wordpress.org/Function_Reference/force_balance_tags By default, it gets used on the Excerpt and Comments If you look at its source, it actually uses REGEX: https://core.trac.wordpress.org/browser/tags/4.1/src/wp-includes/formatting.php#L1453

SIDENOTE I assume the reason they fallback (not ideal) to using REGEX is because of WP's need to run on almost any host's PHP configuration imaginable (i.e. not wanting to load a library in case that PHP extension is disabled). You could choose to load a PHP library designed to do this in your own solution. /SIDENOTE

It's a whole lot of code to be ran every time is_singular(). As such, I'm not sure it'd be worth it to use as inspiration for some complex PHP solution when your site is likely already using JS/jQuery.

  • Is this P class somehow needing to work even on non-JS-enabled browsers?
  • Are you an über-performance guru? If yes, do you have any jQuery in use on your site currently (e.g. slider, motion, etc)? If yes, the minimalistic jQuery needed to do this is not going to add bloat. Just go with it.
  • If you do NOT have jQuery loaded on front-end, we could come up with a pure JavaScript way to accomplish this (i.e. not via jQuery)

Please share your thoughts.


Wordpress uses the autop function to add <p></p> to line breaks in your content.

The autop function can be found on line 373 in formatting.php. An option is to disable the default autop (see below), create a new version of the function adding your class, and then apply it to your content (see the link below for that too). I guess you'd be adding a regex around line 442 to do that.

If you just want to remove the effect of the paragraph you can disable autop like this:

remove_filter( 'the_content', 'wpautop' );
remove_filter( 'the_excerpt', 'wpautop' );

Which can be found on the autop page:

  • not going to downvote, but messing up with autop is a very dangerous thing, and it will not solve the case where the html is generated by a shortcode. Commented Feb 9, 2015 at 6:57
  • I'm not suggesting messing with autop, I'm suggesting creating a custom function based on autop, which is completely ok, and not dangerous. I don't see shortcode mentioned in the question.
    – Steven
    Commented Feb 9, 2015 at 10:21
  • which is exactly the same as messing with autop, and you don't see spans and links mentioned as well.... Commented Feb 9, 2015 at 10:23
  • If writing a custom function is called 'messing' then I guess you're right.
    – Steven
    Commented Feb 9, 2015 at 14:01
  • 1
    autop is optional, if you see the answer you'll see a reference to removing autop provided by Wordpress. Wordpress is built to be flexible and allow developers to modify it's behaviour for custom needs. This is one of them. If I were suggesting touching the core files you'd have a point, but as I'm not, you don't.
    – Steven
    Commented Feb 9, 2015 at 16:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.