39

I have searched high and low for a simple solution to this, but to no avail. Wordpress keeps on wrapping my images in p tags and because of the eccentric nature of the layout for a site I am working on, this is highly annoying.

I have created a jQuery solution to unwrap images, but it isn't that great. It lags because of other stuff loading on the page and so the changes are slow to be made. Is there a way to prevent Wordpress wrapping just images with p tags? A hook or filter perhaps that can be run.

This is happening when uploading an image and then inserting it into the WYSIWYG editor. Manually going into the code view and removing the p tags is not an option as the client is not that technically inept.

I understand that images are inline, but the way I have the site coded images are inside of divs and set to block, so they are valid code.

1

11 Answers 11

41

here's what we did yesterday on a client site that we were having this exact problem with... I created a quick filter as a plugin and activated it.

<?php
/*
Plugin Name: Image P tag remover
Description: Plugin to remove p tags from around images in content outputting, after WP autop filter has added them. (oh the irony)
Version: 1.0
Author: Fublo Ltd
Author URI: http://fublo.net/
*/

function filter_ptags_on_images($content)
{
    // do a regular expression replace...
    // find all p tags that have just
    // <p>maybe some white space<img all stuff up to /> then maybe whitespace </p>
    // replace it with just the image tag...
    return preg_replace('/<p>(\s*)(<img .* \/>)(\s*)<\/p>/iU', '\2', $content);
}

// we want it to be run after the autop stuff... 10 is default.
add_filter('the_content', 'filter_ptags_on_images');

If you drop that into a php file in your /wp-content/plugins folder and then activate it, it should remove the p tags from any para that just contains an image.

I'm not sure how strong the regexp is in terms of if it will fail with outputs from other editors - for example if the img tag is closed with just > it will fail. If anyone has anything stronger, that would be really helpful.

Cheers,

James

--- Improved filter ---

To work with images that are wrapped in links, it keeps the links in the output and removes the p tags.

return preg_replace('/<p>\s*(<a .*>)?\s*(<img .* \/>)\s*(<\/a>)?\s*<\/p>/iU', '\1\2\3', $content);
11
  • Without a doubt, this is the proper answer. Thanks James, I tried it and it works awesomely. Commented Feb 3, 2011 at 3:25
  • Hi @Dwayne - thanks for the feedback. I've added an improved filter that will handle links, we're now using it on our client site.
    – jamesc
    Commented Feb 4, 2011 at 17:29
  • You should definitely put this in the Wordpress plugin repository. A quick google search shows a lot of people are having this problem with no good solution. Commented Aug 29, 2014 at 13:19
  • 2
    Note that this won't work with HTML5 default img markup, i.e. <img ...> without the closing slash. It's better to make that optional in your regex. Or better yet, you can leave it out as .* will take care of it. Commented Mar 10, 2015 at 19:41
  • Has anyone made it work for <img ...> without />?
    – Runnick
    Commented Dec 17, 2017 at 13:41
13

Basically you need to make WordPress treat img like block-level element for the purpose of formatting. Such elements are hardcoded in wpautop() and list is unfortunately not filtered.

What I would do is:

  1. Fork wpautop() under different name.
  2. Add img to regexp in $allblocks variable.
  3. Remove wpautop from the_content filter.
  4. Add your forked version to the_content.
  5. You might need to play with priority and possibly remove and re-add other filters if something breaks because of changed processing order.
2
  • I am going to try this approach. I never thought of actually adding the img tag to the allblocks variable, that's a genius idea. I'll see how I go. Commented Jan 17, 2011 at 10:51
  • Worked well at first, then I hit the scenario when an image is within an anchor tag and both are already inside a paragraph (so p > img > a). With img treated as a block wp-autop closes the paragraph tag out before the img tag starts, busting the layout.
    – benz001
    Commented Jul 19, 2014 at 2:53
2

maybe this will help

remove_filter('the_content', 'wpautop')

But then you are going to add the paragraphs for everything else manually.

1
  • I considered this approach, but because the layout is eccentric as I said it relies heavily on needing p tags. As I am doing a 2 column text thing where p tags are floated left to give the appearance of 2 columns of text. So you can see why a p tag wrapping an image would be a problem because it's floating that too. Commented Jan 17, 2011 at 10:49
2

This post is a little old, but there is a much simpler solution, barring CSS on your end.

Wrapping the img tag in a div has little negative effect.

1

Soska have given one/easy way.

But what I do is, extract image from content and display it separately.

2
  • I did consider this as well and it is still an option. However because it is only one image to save all of the hassle, I might just use featured post thumbnails instead which would let me control how the image displays. Commented Jan 17, 2011 at 10:52
  • also you can add custom field to the post/page, like thumb image and save the image path in its value...
    – Avinash
    Commented Jan 17, 2011 at 11:09
1

I developed a plugin that fixed this exact issue: http://wordpress.org/extend/plugins/unwrap-images/

It's better than setting margin, or diving right into the Wordpress code for those who don't want to mess with code because it uses jQuery's native unwrap function to unwrap all images of their p tags.

Hope this helps someone! Cheers, Brian

1
  • apparently still has 30+ active installations :D
    – Julix
    Commented May 6, 2019 at 18:41
1

Accepted answer helped me with just the images but the revised code doesn't handle linked images well at my site. This blog post has a code that works perfectly.

Here's the code:

function wpautop_forked($pee, $br = 1) {

if ( trim($pee) === '' )
return '';
$pee = $pee . "\n"; // just to make things a little easier, pad the end
$pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
// Space things out a little
$allblocks = '(?:table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li
|pre|select|option|form|map|area|blockquote|img|address|math|style|input
|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer
|nav|figure|figcaption|details|menu|summary)';
$pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee);
$pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee);
$pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines
if ( strpos($pee, '<object') !== false ) {
$pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed
$pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee);
}
$pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates
// make paragraphs, including one at the end
$pees = preg_split('/\n\s*\n/', $pee, -1, PREG_SPLIT_NO_EMPTY);
$pee = '';
foreach ( $pees as $tinkle )
$pee .= '<p>' . trim($tinkle, "\n") . "</p>\n";
$pee = preg_replace('|<p>\s*</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace
$pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag
$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists
$pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee);
$pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee);
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee);
$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee);
if ($br) {
$pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", "<WPPreserveNewline />", $matches[0]);'), $pee);
$pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks
$pee = str_replace('<WPPreserveNewline />', "\n", $pee);
}
$pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee);
$pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee);
if (strpos($pee, '<pre') !== false)
$pee = preg_replace_callback('!(<pre[^>]*>)(.*?)</pre>!is', 'clean_pre', $pee );
$pee = preg_replace( "|\n</p>$|", '</p>', $pee );

return $pee;
}

remove_filter('the_content', 'wpautop');
add_filter('the_content', 'wpautop_forked');

Cheers!

1

I’m not an expert but just spent the hole afternoon trying to solve de img wraped in p tags and this worked for me.

I am working on a wordpress based theme and just added this to the functions.js file

Jquery function unwrap

> $(document).ready(function (){
> 
> // for images wraped in a tags
> 
> $(‘.entry a’).unwrap(‘p’);
> 
> //for images wraped in just p tags
> $(‘.entry img’).unwrap(‘p’);

now I can work p and img seperately.

Also can add a div with a different class arround the img using this:

$(document).ready(function (){

$('.entry img').wrap('<div class="justImg"></div>');

this last one didn’t solved my problem because I wanted to make p tags with display:none; so I really had to take those img out of there.

2
  • 3
    Do you really use curly quotes? :)
    – fuxia
    Commented Feb 18, 2013 at 19:05
  • I did consider this approach first way back when, but the thought of unnecessary DOM manipulation via jQuery was too much of a risk and potential unneeded overhead when you can do this in PHP with tricky regular expressions. Commented Feb 19, 2013 at 23:29
1

Put yout image inside a <div> tag, without any whitespace chars between them. So Instead of :

<div class="your_container">
    <div class="element1">...</div>
    <div class="element2">...</div>
    <img src="image.jpg" />
</div>

You write this:

<div class="your_container">
    <div class="element1">...</div>
    <div class="element2">...</div>
    <div><img src="image.jpg" /></div>
</div>

I've had the same problem with <a> elements, and this solved for me.

0

Depending on the post, another solution could be to use the WP Unformatted plugin to disable the auto-p function on a per post basis.

2
  • That's pretty useful, although the only caveat I can see is that if you want images to not have P tags but also have text within your page, it's going to be one massive mess. This would probably be good for posts that just have images and maybe a few lines of text. Still, useful. Commented Feb 23, 2011 at 1:59
  • Yup, that’s why I said it depends on the post.
    – Synetech
    Commented Feb 23, 2011 at 2:48
0

In case someone is look for a quick and dirty way to fix this for any tag here's what I did:

  1. go to wp-content/formatting.php
  2. find wpautop function. (in case you missed it, it's WP-AUTO-P, get it?)
  3. fins the "all blocks" variable, should be something like $allblocks = '(?:table|thead|tfoot|capti...
  4. at the end add the block you wish to omit - img, a, etc... for example if it ends in (...)menu|summary)'; change to (...)menu|summary|a)'; to add the a tag and avoid autopeeing it. Note the pipe | separator - it's regex syntax!

That's it, happy Wordpressing!

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.