Usando jQuery para reemplazar una etiqueta con otra

100

Objetivo:

Usando jQuery, estoy tratando de reemplazar todas las apariciones de:

<code> ... </code>

con:

<pre> ... </pre>

Mi solución:

Llegué tan lejos como lo siguiente,

$('code').replaceWith( "<pre>" + $('code').html() + "</pre>" );

El problema con mi solución:

pero el problema es que está reemplazando todo entre las etiquetas de "código" (segunda, tercera, cuarta, etc.) con el contenido entre las primeras etiquetas de "código".

p.ej

<code> A </code>
<code> B </code>
<code> C </code>

se convierte en

<pre> A </pre>
<pre> A </pre>
<pre> A </pre>

Creo que necesito usar "esto" y algún tipo de función, pero me temo que todavía estoy aprendiendo y realmente no entiendo cómo armar una solución.

Jon
fuente
Se corrigió la solución de envolver-desenvolver ahora jon - compruébalo;)
Jens Roland

Respuestas:

157

Puede pasar una función a .replaceWith [docs] :

$('code').replaceWith(function(){
    return $("<pre />", {html: $(this).html()});
});

Dentro de la función, se thisrefiere al codeelemento procesado actualmente .

MANIFESTACIÓN

Actualización: No hay una gran diferencia de rendimiento , pero en caso de que los codeelementos tengan otros elementos secundarios HTML, agregar los elementos secundarios en lugar de serializarlos parece más correcto:

$('code').replaceWith(function(){
    return $("<pre />").append($(this).contents());
});
Felix Kling
fuente
1
Esta parece ser la solución más elegante, aunque admito que no entiendo lo que sucede dentro de la función replaceWith. Me encantaría saber más. es decir, ¿cómo se asignan las etiquetas de apertura y cierre? ¿El espacio en <pre /> logra eso?
jon
2
@jon: Es una forma abreviada de crear nuevos elementos. Puede encontrar más información aquí: api.jquery.com/jQuery/#jQuery2 . jQuery recorre todos los elementos y ejecuta la función para cada uno de ellos (lo mismo que .eachestá haciendo).
Felix Kling
@jon: Pero revisa la solución de Jens nuevamente, él la arregló.
Felix Kling
1
+1 para un buen truco: esto se puede usar para mucho más que una simple sustitución de etiqueta por etiqueta.
Jens Roland
@FelixKling ¿No sobrescribiría esto los atributos de las etiquetas de código?
Tewathia
80

Esto es mucho mejor:

$('code').contents().unwrap().wrap('<pre/>');

Aunque es cierto que la solución de Felix Kling es aproximadamente el doble de rápida :

Jens Roland
fuente
4
funcionó a las mil maravillas, para mis ojos profanos, esta parece ser la solución más eficiente. :)
jon
aha. sí, tenían razón. tampoco funcionó para mí. gracias por coger eso!
jon
1
FWIW, $('code').children()devolverá un objeto jQuery vacío para el ejemplo anterior, porque los codeelementos no tienen nodos de elementos secundarios. Aunque funciona si hay elementos secundarios.
Felix Kling
1
Fijo: cierto, cuando el contenido consta de un solo nodo de texto, children()no funcionará. Usar en su contents()lugar funciona perfectamente. jsfiddle.net/7Yne9
Jens Roland
1
Aquí hay una versión de jsperf que no rompe la página: jsperf.com/substituting-one-tag-for-another-with-jquery/2 y ves que la diferencia de velocidad ya no es tan grande. Su solución es más lenta porque realiza dos manipulaciones DOM por elemento: unwrapelimina el padre y agrega los hijos al árbol, los wrapsepara de nuevo y agrega un nuevo padre.
Felix Kling
26

Es correcto que siempre obtendrás el codecontenido del primero , porque$('code').html() siempre hará referencia al primer elemento, donde sea que lo uses.

En su lugar, puede usar .eachpara iterar sobre todos los elementos y cambiar cada uno individualmente:

$('code').each(function() {
    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );
    // this function is executed for all 'code' elements, and
    // 'this' refers to one element from the set of all 'code'
    // elements each time it is called.
});
pimvdb
fuente
12

Prueba esto:

$('code').each(function(){

    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );

});

http://jsfiddle.net/mTGhV/

Kokos
fuente
Guau. ustedes llegaron con la misma solución exacta con 2 segundos de diferencia. hay pequeñas diferencias de formato. No estoy seguro de cuál votar; realmente no me gusta el espacio entre la función y () en la respuesta de thomas y las 2 líneas de espacio vacío en la respuesta de kokos son bastante precisas + debería haber un espacio entre () y el {
Michael Bylstra
11

¿Qué tal esto?

$('code').each(function () {
    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );
});
Tae-Sung Shin
fuente
6

Construyendo sobre la respuesta de Felix.

$('code').replaceWith(function() {
    var replacement = $('<pre>').html($(this).html());
    for (var i = 0; i < this.attributes.length; i++) {
        replacement.attr(this.attributes[i].name, this.attributes[i].value);
    }
    return replacement;
});

Esto reproducirá los atributos de las codeetiquetas en el reemplazo.pre etiquetas de .

Editar: esto reemplazará incluso las codeetiquetas que están dentro innerHTMLde otras codeetiquetas.

function replace(thisWith, that) {
    $(thisWith).replaceWith(function() {
        var replacement = $('<' + that + '>').html($(this).html());
        for (var i = 0; i < this.attributes.length; i++) {
            replacement.attr(this.attributes[i].name, this.attributes[i].value);
        }
        return replacement;
    });
    if ($(thisWith).length>0) {
        replace(thisWith, that);
    }
}

replace('code','pre');
tewathia
fuente
2
la mejor respuesta hasta ahora. Los otros son prácticamente todos iguales, lo que es realmente extraño que la gente los vote. Felicitaciones por ser el único que pensó en los atributos.
Guilherme David da Costa
2

Si estuviera usando JavaScript vainilla, haría lo siguiente:

  • Crea el nuevo elemento
  • Mueve los hijos del elemento antiguo al nuevo elemento.
  • Inserte el nuevo elemento antes que el antiguo
  • Eliminar el elemento antiguo

Aquí está jQuery equivalente de este proceso:

$("code").each(function () {
    $("<pre></pre>").append(this.childNodes).insertBefore(this);
    $(this).remove();
});

Aquí está la URL de jsperf:
http://jsperf.com/substituting-one-tag-for-another-with-jquery/7

PD: Todas las soluciones que usan .html()o .innerHTMLson destructivas .

Salman A
fuente
2

Otra forma corta y fácil:

$('code').wrapInner('<pre />').contents();
Fabian von Ellerts
fuente
0
$('code').each(function(){
    $(this).replaceWith( "<pre>" + $(this).html()  + "</pre>" );
    });

Mejor y limpia manera.

Nimek
fuente
0

Puede usar la función html de jQuery. A continuación se muestra una muestra que reemplaza una etiqueta de código con una preetiqueta conservando todos los atributos del código.

    $('code').each(function() {
        var temp=$(this).html();
        temp=temp.replace("code","pre");
        $(this).html(temp);
    });

Esto podría funcionar con cualquier conjunto de etiquetas de elementos html que debían intercambiarse conservando todos los atributos de la etiqueta anterior.

Arnab C.
fuente
0

jqueryComplemento hecho , manteniendo los atributos también.

$.fn.renameTag = function(replaceWithTag){
  this.each(function(){
        var outerHtml = this.outerHTML;
        var tagName = $(this).prop("tagName");
        var regexStart = new RegExp("^<"+tagName,"i");
        var regexEnd = new RegExp("</"+tagName+">$","i")
        outerHtml = outerHtml.replace(regexStart,"<"+replaceWithTag)
        outerHtml = outerHtml.replace(regexEnd,"</"+replaceWithTag+">");
        $(this).replaceWith(outerHtml);
    });
    return this;
}

Uso:

$('code').renameTag('pre')
JagsGorrión
fuente
0

Todas las respuestas dadas aquí asumen (como indica el ejemplo de pregunta) que no hay atributos en la etiqueta. Si la respuesta aceptada se ejecuta en esto:

<code class='cls'>A</code>

si será reemplazado con

<pre>A</pre>

¿Qué sucede si también desea conservar los atributos, que es lo que significaría reemplazar una etiqueta ...? Esta es la solucion:

$("code").each( function(){
    var content = $( "<pre>" );

    $.each( this.attributes, function(){ 
         content.attr( this.name, this.value );
    } );

    $( this ).replaceWith( content );
} );
Patricio
fuente
Sé que esto tiene un par de años, pero pensé en mencionar ... que olvidó mantener el html del elemento anterior. En su fragmento de código, simplemente está creando un nuevo elemento con los atributos sin copiar tampoco a ninguno de los elementos secundarios.
Un amigo
eso se haría agregandocontent.html( this.html )
patrick