Utiliser jQuery pour remplacer une balise par une autre

101

Objectif:

En utilisant jQuery, j'essaye de remplacer toutes les occurrences de:

<code> ... </code>

avec:

<pre> ... </pre>

Ma solution:

Je suis allé jusqu'à ce qui suit,

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

Le problème avec ma solution:

mais le problème est qu'il remplace tout ce qui se trouve entre les balises "code" (deuxième, troisième, quatrième, etc.) par le contenu entre les premières balises "code".

par exemple

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

devient

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

Je pense que j'ai besoin d'utiliser «ceci» et une sorte de fonction, mais j'ai peur d'apprendre encore et de ne pas vraiment comprendre comment reconstituer une solution.

Jon
la source
Correction de la solution wrap-unwrap now jon - check it out;)
Jens Roland

Réponses:

157

Vous pouvez transmettre une fonction à .replaceWith [docs] :

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

À l'intérieur de la fonction, thisfait référence à l' codeélément actuellement traité .

DEMO

Mise à jour: il n'y a pas de grande différence de performances , mais dans le cas où les codeéléments ont d'autres enfants HTML, ajouter les enfants au lieu de les sérialiser semble plus correct:

$('code').replaceWith(function(){
    return $("<pre />").append($(this).contents());
});
Félix Kling
la source
1
cela semble être la solution la plus élégante, mais j'admets que je ne comprends pas ce qui se passe à l'intérieur de la fonction replaceWith. aimerait en savoir plus. c'est-à-dire comment attribuer les balises d'ouverture ET de fermeture? est-ce que l'espace dans <pre /> accomplit cela?
jon
2
@jon: C'est un raccourci pour créer de nouveaux éléments. Plus d'informations peuvent être trouvées ici: api.jquery.com/jQuery/#jQuery2 . jQuery boucle sur tous les éléments et exécute la fonction pour chacun d'eux (même ce que .eachfait).
Felix Kling
@jon: Mais revérifiez la solution de Jens, il l'a corrigée.
Felix Kling
1
+1 pour une belle astuce - cela peut être utilisé pour beaucoup plus qu'une simple substitution tag par tag.
Jens Roland
@FelixKling Cela n'écraserait-il pas les attributs des balises de code?
tewathia
80

C'est beaucoup plus agréable:

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

Certes, la solution de Felix Kling est environ deux fois plus rapide :

Jens Roland
la source
4
a fonctionné comme un charme, à mes yeux profanes, cela semble être la solution la plus efficace. :)
jon
aha. oui, vous aviez raison. cela n'a pas fonctionné pour moi non plus. merci d'avoir attrapé ça!
jon
1
FWIW, $('code').children()retournera un objet jQuery vide pour l'exemple ci-dessus, car les codeéléments n'ont pas de nœuds d'élément enfant. Bien que cela fonctionne s'il y a des éléments enfants.
Felix Kling
1
Corrigé - vrai, lorsque le contenu se compose d'un seul nœud de texte, children()ne fonctionnera pas. Utiliser à la contents()place fonctionne parfaitement. jsfiddle.net/7Yne9
Jens Roland
1
Voici une version de jsperf qui ne brise pas la page: jsperf.com/substituting-one-tag-for-another-with-jquery/2 et vous voyez que la différence de vitesse n'est plus si énorme. Votre solution est plus lente car vous effectuez deux manipulations DOM par élément: unwrapsupprime le parent et ajoute les enfants à l'arborescence, les wrapdétache à nouveau et ajoute un nouveau parent.
Felix Kling
26

Il est vrai que vous obtiendrez toujours le codecontenu du premier , car il $('code').html()fera toujours référence au premier élément, où que vous l'utilisiez.

Au lieu de cela, vous pouvez utiliser .eachpour itérer sur tous les éléments et modifier chacun individuellement:

$('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
la source
12

Essaye ça:

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

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

});

http://jsfiddle.net/mTGhV/

Kokos
la source
sensationnel. vous avez trouvé exactement la même solution à moins de 2 secondes d'intervalle. il existe de minuscules différences de formatage. Je ne sais pas lequel voter pour - je n'aime vraiment pas l'espace entre la fonction et () dans la réponse de Thomas et les 2 lignes d'espace vide dans la réponse kokos sont assez pointillées + il devrait y avoir un espace entre le () et le {
Michael Bylstra
11

Que dis-tu de ça?

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

S'appuyant sur la réponse 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;
});

Cela reproduira les attributs des codebalises dans les prebalises de remplacement .

Modifier: Cela remplacera même les codebalises qui se trouvent à l'intérieur innerHTMLdes autres codebalises.

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
la source
2
meilleure réponse à ce jour. Les autres sont à peu près tous les mêmes, ce qui est vraiment étrange que les gens les approuvent. Félicitations pour être le seul à avoir pensé aux attributs.
Guilherme David da Costa
2

Si vous utilisiez du JavaScript vanille, vous:

  • Créer le nouvel élément
  • Déplacez les enfants de l'ancien élément dans le nouvel élément
  • Insérez le nouvel élément avant l'ancien
  • Supprimer l'ancien élément

Voici l'équivalent jQuery de ce processus:

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

Voici l'URL jsperf:
http://jsperf.com/substituting-one-tag-for-another-with-jquery/7

PS: Toutes les solutions qui utilisent .html()ou .innerHTMLsont destructives .

Salman A
la source
2

Un autre moyen court et facile:

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

Meilleure et propre manière.

Nimek
la source
0

Vous pouvez utiliser la fonction html de jQuery. Voici un exemple qui remplace une balise de code par une balise pré tout en conservant tous les attributs du code.

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

Cela pourrait fonctionner avec n'importe quel ensemble de balises d'élément html qui devaient être permutées tout en conservant tous les attributs de la balise précédente.

Arnab C.
la source
0

Fait jqueryplug - in, en maintenant les attributs aussi.

$.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;
}

Usage:

$('code').renameTag('pre')
JagsSparrow
la source
0

Toutes les réponses données ici supposent (comme l'indique l'exemple de question) qu'il n'y a aucun attribut dans la balise. Si la réponse acceptée est exécutée sur ceci:

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

si sera remplacé par

<pre>A</pre>

Que faire si vous souhaitez également conserver les attributs - ce que signifierait le remplacement d'une balise ...? Voici la solution:

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

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

    $( this ).replaceWith( content );
} );
patrick
la source
Je sais que cela remonte à quelques années, mais je pensais juste mentionner ... vous avez oublié de garder le html de l'élément précédent. Dans votre extrait de code, vous créez simplement un nouvel élément avec les attributs sans copier également aucun des enfants.
Un ami
cela se ferait en ajoutantcontent.html( this.html )
patrick