Evitar que WordPress envuelva las imágenes en etiquetas "P"
He buscado por todas partes una solución simple para esto, pero sin éxito. WordPress sigue envolviendo mis imágenes en etiquetas p y debido a la naturaleza excéntrica del diseño del sitio en el que estoy trabajando, esto es muy molesto.
He creado una solución con jQuery para desenvolver las imágenes, pero no es muy buena. Se retrasa debido a otras cosas que se cargan en la página y por lo tanto los cambios son lentos. ¿Hay alguna manera de evitar que WordPress envuelva solo las imágenes con etiquetas p? Tal vez un hook o filtro que se pueda ejecutar.
Esto sucede cuando se sube una imagen y luego se inserta en el editor WYSIWYG. Ir manualmente a la vista de código y eliminar las etiquetas p no es una opción ya que el cliente no tiene esos conocimientos técnicos.
Entiendo que las imágenes son elementos en línea, pero por la forma en que he codificado el sitio, las imágenes están dentro de divs y configuradas como block, por lo que son código válido.

Aquí está lo que hicimos ayer en el sitio de un cliente con el que teníamos este mismo problema... Creé un filtro rápido como plugin y lo activé.
<?php
/*
Plugin Name: Eliminador de etiquetas P en imágenes
Description: Plugin para eliminar etiquetas p alrededor de imágenes en el contenido, después de que el filtro autop de WP las haya añadido. (qué ironía)
Version: 1.0
Author: Fublo Ltd
Author URI: http://fublo.net/
*/
function filter_ptags_on_images($content)
{
// realizar un reemplazo con expresión regular...
// encontrar todas las etiquetas p que solo contengan
// <p>quizá algo de espacio en blanco<img todo hasta /> luego quizá espacio en blanco </p>
// reemplazarlo solo con la etiqueta de imagen...
return preg_replace('/<p>(\s*)(<img .* \/>)(\s*)<\/p>/iU', '\2', $content);
}
// queremos que se ejecute después del filtro autop... 10 es el valor por defecto.
add_filter('the_content', 'filter_ptags_on_images');
Si colocas eso en un archivo php dentro de tu carpeta /wp-content/plugins y luego lo activas, debería eliminar las etiquetas p de cualquier párrafo que solo contenga una imagen.
No estoy seguro de lo fuerte que es la expresión regular en términos de si fallará con salidas de otros editores - por ejemplo, si la etiqueta img se cierra solo con > fallará. Si alguien tiene algo más robusto, sería de gran ayuda.
Saludos,
James
--- Filtro mejorado ---
Para funcionar con imágenes envueltas en enlaces, mantiene los enlaces en la salida y elimina las etiquetas p.
return preg_replace('/<p>\s*(<a .*>)?\s*(<img .* \/>)\s*(<\/a>)?\s*<\/p>/iU', '\1\2\3', $content);

Sin lugar a dudas, esta es la respuesta correcta. Gracias James, lo probé y funciona increíblemente bien.

Hola @Dwayne - gracias por tus comentarios. He añadido un filtro mejorado que manejará los enlaces, ahora lo estamos usando en el sitio de nuestro cliente.

Definitivamente deberías publicar esto en el repositorio de plugins de Wordpress. Una búsqueda rápida en Google muestra que mucha gente tiene este problema sin una buena solución.

Ten en cuenta que esto no funcionará con el marcado HTML5 predeterminado de img
, es decir, <img ...>
sin la barra diagonal de cierre. Es mejor hacer que sea opcional en tu expresión regular. O mejor aún, puedes omitirlo ya que .*
se encargará de ello.

El siguiente código funcionó para sacar una imagen <img> de un párrafo <p> incluso si va seguida de texto:
return preg_replace('/<p>(\s*)(<img .*>)/iU', "$2<p>", $content);

@jamesc ¿Cómo se podría ampliar esto para hacer lo mismo con otros elementos, como <iframe>?

Básicamente, necesitas hacer que WordPress trate a img como un elemento a nivel de bloque para fines de formato. Estos elementos están codificados directamente en wpautop()
y la lista desafortunadamente no se puede filtrar.
Lo que yo haría es:
- Hacer un fork de
wpautop()
con un nombre diferente. - Añadir
img
a la expresión regular en la variable$allblocks
. - Eliminar
wpautop
del filtrothe_content
. - Añadir tu versión modificada a
the_content
. - Puede que necesites ajustar la prioridad y posiblemente eliminar y volver a añadir otros filtros si algo se rompe debido al cambio en el orden de procesamiento.

Voy a probar este enfoque. Nunca se me ocurrió agregar la etiqueta img a la variable allblocks, es una idea genial. Veré cómo me va.

Funcionó bien al principio, pero luego me encontré con el escenario cuando una imagen está dentro de una etiqueta de anclaje y ambas ya están dentro de un párrafo (es decir, p > img > a). Al tratar img como un bloque, wp-autop cierra la etiqueta de párrafo antes de que comience la etiqueta img, lo que arruina el diseño.

Consideré este enfoque, pero debido a que el diseño es excéntrico como mencioné, depende en gran medida de necesitar etiquetas p. Estoy haciendo un diseño de texto de 2 columnas donde las etiquetas p están flotando a la izquierda para dar la apariencia de 2 columnas de texto. Entonces puedes ver por qué una etiqueta p envolviendo una imagen sería un problema porque también la estaría flotando.

También consideré esta opción y sigue siendo una posibilidad. Sin embargo, como es solo una imagen para evitar toda la complicación, podría simplemente usar miniaturas de entradas destacadas que me permitirían controlar cómo se muestra la imagen.

Desarrollé un plugin que soluciona este problema exacto: http://wordpress.org/extend/plugins/unwrap-images/
Es mejor que configurar márgenes o meterse directamente en el código de WordPress para aquellos que no quieren lidiar con código, ya que utiliza la función nativa unwrap de jQuery para eliminar las etiquetas p que envuelven a las imágenes.
¡Espero que esto le ayude a alguien! Saludos, Brian

La respuesta aceptada me ayudó solo con las imágenes, pero el código revisado no maneja bien las imágenes enlazadas en mi sitio. Esta publicación de blog tiene un código que funciona perfectamente.
Aquí está el código:
function wpautop_forked($pee, $br = 1) {
if ( trim($pee) === '' )
return '';
$pee = $pee . "\n"; // para facilitar las cosas, rellenamos el final
$pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee);
// Espaciar un poco los elementos
$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); // saltos de línea multiplataforma
if ( strpos($pee, '<object') !== false ) {
$pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no contenido dentro de object/embed
$pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee);
}
$pee = preg_replace("/\n\n+/", "\n\n", $pee); // manejar duplicados
// crear párrafos, incluyendo uno al final
$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); // en ciertas condiciones extrañas podría crear un P de solo espacios
$pee = preg_replace('!<p>([^<]+)</(div|address|form)>!', "<p>$1</p></$2>", $pee);
$pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // no aplicar formato a una etiqueta
$pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problema con listas anidadas
$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); // opcionalmente crear saltos de línea
$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');
¡Saludos!

No soy un experto pero pasé toda la tarde intentando resolver el problema de las imágenes envueltas en etiquetas p y esto funcionó para mí.
Estoy trabajando en un tema basado en WordPress y simplemente agregué esto al archivo functions.js
Función jQuery unwrap
> $(document).ready(function (){
>
> // para imágenes envueltas en etiquetas a
>
> $(‘.entry a’).unwrap(‘p’);
>
> // para imágenes envueltas solo en etiquetas p
> $(‘.entry img’).unwrap(‘p’);
Ahora puedo trabajar con las etiquetas p e img por separado.
También se puede agregar un div con una clase diferente alrededor de la imagen usando esto:
$(document).ready(function (){
$('.entry img').wrap('<div class="justImg"></div>');
Esta última no resolvió mi problema porque quería hacer que las etiquetas p tuvieran display:none;, así que realmente tuve que sacar esas imágenes de ahí.

Coloca tu imagen dentro de una etiqueta <div>
, sin ningún carácter de espacio en blanco entre ellas. Así que en lugar de:
<div class="your_container">
<div class="element1">...</div>
<div class="element2">...</div>
<img src="image.jpg" />
</div>
Escribe esto:
<div class="your_container">
<div class="element1">...</div>
<div class="element2">...</div>
<div><img src="image.jpg" /></div>
</div>
He tenido el mismo problema con elementos <a>
, y esto lo solucionó para mí.

Dependiendo de la entrada, otra solución podría ser usar el plugin WP Unformatted para desactivar la función auto-p en una base por entrada.

Eso es bastante útil, aunque la única advertencia que puedo ver es que si quieres que las imágenes no tengan etiquetas P pero también tener texto dentro de tu página, va a ser un gran desorden. Probablemente sería bueno para publicaciones que solo tienen imágenes y tal vez unas pocas líneas de texto. Aún así, útil.

En caso de que alguien esté buscando una forma rápida y sucia de solucionar esto para cualquier etiqueta, esto es lo que hice:
- Ve a wp-content/formatting.php
- Busca la función wpautop. (por si no lo pillaste, es WP-AUTO-P, ¿entiendes?)
- Busca la variable "all blocks", debería ser algo como
$allblocks = '(?:table|thead|tfoot|capti...
- Al final añade el bloque que quieras omitir -
img
,a
, etc... Por ejemplo, si termina en(...)menu|summary)';
cámbialo a(...)menu|summary|a)';
para añadir la etiquetaa
y evitar que le aplique el autopeeing. ¡Fíjate en el separador de barra vertical|
- es sintaxis de regex!
¡Eso es todo, feliz WordPresseo!
