When inserting an image into a WYSIWYG editor it is wrapped in a <p> tag. This is causing several display issues that can not be resolved with CSS alone. I would like help finding the best solution for removing those <p> tags.

Expected Behavior

<p>Some text</p>
<img src="......" />
<p>Some more text</p>

Current Behavior

<p>Some text</p>
<p><img src="......" /></p>
<p>Some more text</p>

Related Discussions
http://stackoverflow.com/questions/5209106/regular-expression-to-remove-...
http://stackoverflow.com/questions/2170075/regular-expression-to-remove-...
http://wordpress.stackexchange.com/questions/14959/how-to-remove-p-tags-...
http://drupal.org/node/390186
http://drupal.org/node/1078622

*I know that removing all filters resolves this issue, but I need this solution for my users, and I must keep those filters enabled for them.

Comments

For anyone else stumbling into this problem there is a work around that is not optimal, but it works.

Add this JS file to your theme.

jQuery(document).ready(function($){
// Removes bottom margin of <p> wrapper that gets inserted around images with caption class
$('p .image-caption-container').each(function() {
// Make this code do what you want.
$(this).parent().css('margin-bottom', '0px');
});
});

This will find all the elements with the "image-caption-container" class. It will then look at the parent class, in this case it's the offending <p> tag. From there you can set the css of the parent tag. The code above removes the bottom margin.

Looks like instead of

$(this).parent().css('margin-bottom', '0px');

we can use

$(this).unwrap();

This will strip out the p tag completely.

The reason the editors wrap <img> tags is that they're inline elements which must be contained by a block type element and are not allowed directly below the body element.
Inside the editor, the contents are placed in a very basic document directly inside the body, so any image elements will have to be wrapped.
But, if there's a block level element wrapping the image (and possibly other elements like below) the editor will not add any extra wrapping.

<div>
<p>Line 1</p>
<img src="..." />
<p>Line 2</p>
</div>

You don't mention which editor you are using, but judging by the code in CKEditor, there's no easy way to tell it that you don't want the content treated as if it's to be placed directly inside the body tag.

Using CSS to make sure the paragraphs around image tags aren't noticable is probably the easiest solution, or use scripts like above - but they won't work if JS is disabled.
One option would be to modify CKEditor's Data Processor so that it wraps all the content in a div or something and then strips it just before writing the final markup, but that's pretty tricky business. Especially since Wysiwyg already wraps the Data Processor to allow cross-editor plugins to work on the content.

Speaking of cross editor plugins... it would be much easier to write a plugin to wrap all of the contents in a div before it's handed over to the editor, and remove the div after the editor is done with it. But I have no time to try that now so I can't guarantee anything.

Correct, I am using CKEditor. However the "Related Discussion" links I provided above are folks looking for a similar solution with several different editors.

The problem I have with CSS is that I'm trying to change the parent element, and to my knowledge there is no way to make that happen purely in CSS, that's why I looked to JS for an interim solution.

Instead of messing with each editor, and correct me if I'm wrong, one way to resolve this would be to have the insert module insert the image with a wrapper element, maybe a <div>.

This is easy to achieve. Cope "insert/templates/image-insert-image.tpl.php" to your admin theme

Then take whatever is there and put a <div> wrapper around it, or your block level element of choice.

<div><img src="<?php print $url ?>" alt="__alt__" title="__title__" class="image-<?php print $style_name ?><?php print $class ? ' ' . $class : '' ?>" /></div>

When adding an image at the beginning of your content you'll end up with a <p>&nbsp;</p> coming before the image, but that can be taken care of with Empty Paragraph Killer http://drupal.org/project/emptyparagraphkiller

Now when using the insert module your images will be inserted with a div wrapper, and no <p> wrapper in sight!

**This div wrapper is lost and <p> tag comes back if you drag and drop the image around in the editor area, so the JS is a good fall back.

@TwoD - I never knew that the body couldn't contain inline elements as direct children, but I did a bit of research and you're right. Thanks for the enlightenment.

That said, my 2 cents:

- wrapping <img>s in <p>s by default is obviously sometimes undesirable due to default or custom styling on paragraphs
- so, perhaps wrapping in a <div> by default would be an improvement (or better yet, a div with a class to indicate its purpose, like, <div class="inline-image"><img src="blah.jpg" /></div>)
- or ideally, there should be configurable options for in which element to wrap these "orphaned" inline elements, and for a custom class to give the wrapper

@tea.time, mind adding <code></code> tags to that post above?

As image inserts is something the editors handle internally I don't think adding wrappers to each img tag is a practical solution, users could easily drag the image out of the wrapper and other image related plugins would not expect the wrappers to be there.

(oops, I saw the missing code tags and then got distracted and forgot to fix at the time of.)

I guess maybe I was thinking more along the lines of a filter...which would happen upon output, when the user doesn't have any control over the content anymore. But if editors are adding the wrapping <p> real-time during editing anyway, I would think using a different tag as a wrapper would be basically the same (as far as the issue of user messing with the wrapper).

@TwoD:

The reason the editors wrap Only local images are allowed. tags is that they're inline elements which must be contained by a block type element and are not allowed directly below the body element.
Inside the editor, the contents are placed in a very basic document directly inside the body, so any image elements will have to be wrapped.
But, if there's a block level element wrapping the image (and possibly other elements like below) the editor will not add any extra wrapping.

I don't think this is true. In HTML5, body's content model is flow content, which includes img.

It wouldn't be a reason to wrap it anyway.

I was not considering HTML5 as Drupal is (or was at the time I wrote that) outputting mostly XHTML.
If HTML5 allows images in the body, I'd expect editors supporting HTML5 to allow that.

I was wondering wether the http://drupal.org/project/simplehtmldom module might help, but I am not sure how to use it. Any ideas?

More on that. I am trying to remove the paragraph tag by making an input filter. This is done primitively by regular expression, not using any parser. However, I have a bug, since it removes the p tag only from the first image. Must be something silly, I know. Any help?

/**
* Process callback for the imageparagraphkillter filter.
*/
function imageparagraphkiller__imageparagraphkiller_process($text, $filter, $format, $langcode, $cache, $cache_id) {
  $pattern = '/<p[^>]*?>(<img[^>]+>)<\/p>/';
  if(preg_match($pattern, $text, $matches)){
foreach($matches as $match){
$original = $match;
$match = str_replace('<p>', '', $match);
$match = str_replace('</p>', '', $match);
$text = str_replace($original, $match, $text);
}
  }
  return $text;
}

Thanks.

Update:

Alex Raskin gave me a regex that works without my bad loop. Here is the code then:

/**
* Process callback for the imageparagraphkillter filter.
*/
function imageparagraphkiller__imageparagraphkiller_process($text, $filter, $format, $langcode, $cache, $cache_id) {
  $text = preg_replace("#<p>\s*(<img [^>]*>)\s*</p>#iUum", "$1", $text);
  return $text;
}

Any comments are welcome.

BTW, what about P tags wrapping DIV tags?...

P tags should never wrap DIV tags, if they do it's a problem in the editor (or some plugin).
Do you have an example of when that happens?

Still, also found http://drupal.org/project/htmlpurifier module. Not sure whether it is a better or 'more right' approach.

@TwoD
When I add a div tag with the div button of the editor (ckeditor) it is inserted inside a p tag.

There is also http://drupal.org/project/wysiwyg_filter, but both of these modules are implemented as text filters running when content is rendered, meaning all the funky markup generated by the editors has still been saved to the database.

I can't make CKEditor insert div tags inside p tags with that button, it always wraps the current paragraph in the div instead. Only tested in IE 8 and Chrome 19 so far.

@TwoD
Wysiwyg Filter deos not solve the extra

tag, afaik.

Update

Current regex is this:

$text = preg_replace("#<p>(\s*(?:<a.*>)?\s*<img .*>\s*(?:</a.*>)?\s*)</p>#Uim", "$1", $text);

Thanks. This would be invaluable for me. I created a custom module with this code but ckeditor continues to add p tags around all my img tags in the Full Editor. Is there anything else I need to be doing?

Alternatively, is there a way to clear an image of its p properties in css? For example, I have some side by side images and text, but this

.node p:after {
    content: ".";
    display: block;
    height: 0;
    clear: both;
    visibility: hidden;
}

causes the images to push down the text. I haven't found a way to cancel this in the img tag.

I came up with a kluge. I created a class: p.ignore-p

p.ignore-p { /* For <p> that is automatically inserted, but you want to ignore. */
  display:inline;
  margin: 0;
  padding: 0;
}

Mark, how are you getting ignore-p to be added?

Go into the plain text editor, and where the <p> wrapper has automatically been added, change it to <p class="ignore-p">.

@Mark123
This is not a legitimate solution for users.

Amir, could you explain your reasoning?

Mark123's solution works well enough for me. Thank you!

@Mark123
What I meant is that we cannot expect end users that do not know HTML to manually remove the p element safely without breaking the markup. This is why we use wysiwyg in the first place ;)

Category:support» bug
Priority:Normal» Major

My main problem is that when rendering a file entity display using media which contains divs, the

tag is being chopped by drupal somehow, of course, it is preventing from outputting invalid HTML, but I really just want to place a image into a div container. There is really no need for a

tag... It makes it impossible to provide usable WYSIWYG templates for the client.

I think this issue deserves more attention as it causes invalid makup. Bumping it to major.

#12 works great! thanks.
There is a nice module Empty paragraph killer.
I patched it using #12: https://drupal.org/node/2079253.
Maybe it can be useful for somebody.