Usando jQuery para substituir uma tag por outra

100

Objetivo:

Usando jQuery, estou tentando substituir todas as ocorrências de:

<code> ... </code>

com:

<pre> ... </pre>

Minha solução:

Eu cheguei até o seguinte,

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

O problema com minha solução:

mas o problema é que ele está substituindo tudo entre as (segunda, terceira, quarta, etc) tags de "código" pelo conteúdo entre as primeiras tags de "código".

por exemplo

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

torna-se

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

Acho que preciso usar "isso" e algum tipo de função, mas ainda estou aprendendo e não entendo realmente como montar uma solução.

Jon
fonte
Corrigida a solução de empacotamento agora jon - confira;)
Jens Roland

Respostas:

157

Você pode passar uma função para .replaceWith [documentos] :

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

Dentro da função, thisrefere-se ao codeelemento processado atualmente .

DEMO

Atualização: não há grande diferença de desempenho , mas caso os codeelementos tenham outros filhos HTML, anexar os filhos em vez de serializá-los parece ser mais correto:

$('code').replaceWith(function(){
    return $("<pre />").append($(this).contents());
});
Felix Kling
fonte
1
esta parece ser a solução mais elegante, embora eu admita que não entendo o que está acontecendo dentro da função replaceWith. adoraria ouvir mais. ou seja, como é atribuir as tags de abertura e fechamento? o espaço em <pre /> cumpre isso?
junho de
2
@jon: É um atalho para a criação de novos elementos. Mais informações podem ser encontradas aqui: api.jquery.com/jQuery/#jQuery2 . jQuery faz um loop em todos os elementos e executa a função para cada um deles (o mesmo que .eachestá fazendo).
Felix Kling de
@jon: Mas verifique a solução de Jens novamente, ele consertou.
Felix Kling de
1
+1 para um bom truque - isso pode ser usado para muito mais do que uma simples substituição tag por tag.
Jens Roland
@FelixKling Isso não substituiria os atributos das tags de código?
tewathia de
80

Isso é muito melhor:

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

Embora reconhecidamente a solução de Felix Kling seja aproximadamente duas vezes mais rápida :

Jens Roland
fonte
4
funcionou como um encanto, aos meus olhos leigos esta parece ser a solução mais eficiente. :)
jon
aha. sim, vocês estavam certos. não funcionou para mim também. obrigado por pegar isso!
junho de
1
FWIW, $('code').children()retornará um objeto jQuery vazio para o exemplo acima, porque os codeelementos não têm nós de elemento filho. Embora funcione se houver elementos filho.
Felix Kling de
1
Fixo - verdadeiro, quando o conteúdo consiste em um único nó de texto, children()não funciona. Usar em contents()vez disso funciona perfeitamente. jsfiddle.net/7Yne9
Jens Roland
1
Aqui está uma versão do jsperf que não quebra a página: jsperf.com/substituting-one-tag-for-another-with-jquery/2 e você vê que a diferença de velocidade não é mais tão grande. Sua solução é mais lenta porque você executa duas manipulações de DOM por elemento: unwrapremove o pai e adiciona os filhos à árvore, wrapdesanexa-os novamente e adiciona um novo pai.
Felix Kling de
26

É correto que você obterá sempre o codeconteúdo do primeiro , pois $('code').html()sempre se referirá ao primeiro elemento, onde quer que o use.

Em vez disso, você pode usar .eachpara iterar todos os elementos e alterar cada um 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
fonte
12

Experimente isto:

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

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

});

http://jsfiddle.net/mTGhV/

Kokos
fonte
Uau. vocês chegaram com a mesma solução exata com 2 segundos de diferença. existem pequenas diferenças de formatação. Não tenho certeza de qual votar - eu realmente não gosto do espaço entre a função e () na resposta de Thomas e as 2 linhas de espaço vazio na resposta de kokos são bastante pontuais + deve haver um espaço entre () e o {
Michael Bylstra
11

Que tal agora?

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

Construindo a resposta 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;
});

Isso reproduzirá os atributos das codetags nas pretags de substituição .

Editar: Isso substituirá até mesmo as codetags que estão dentro innerHTMLde outras codetags.

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
fonte
2
melhor resposta até agora. Os outros são praticamente todos iguais, o que é realmente estranho que as pessoas votem neles. Parabéns por ser o único que pensou nos atributos.
Guilherme David da Costa
2

Se você estivesse usando o JavaScript vanilla, você:

  • Crie o novo elemento
  • Mova os filhos do elemento antigo para o novo elemento
  • Insira o novo elemento antes do antigo
  • Remova o elemento antigo

Aqui está o equivalente em jQuery deste processo:

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

Aqui está o URL do jsperf:
http://jsperf.com/substituting-one-tag-for-another-with-jquery/7

PS: Todas as soluções que usam .html()ou .innerHTMLsão destrutivas .

Salman A
fonte
2

Outra maneira curta e fácil:

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

Maneira melhor e limpa.

Nimek
fonte
0

Você pode usar a função html do jQuery. Abaixo está um exemplo que substitui uma tag de código por uma tag pre, mantendo todos os atributos do código.

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

Isso poderia funcionar com qualquer conjunto de tags de elemento html que precisassem ser trocadas enquanto retinham todos os atributos da tag anterior.

Arnab C.
fonte
0

jqueryPlugin feito , mantendo também atributos.

$.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')
JagsSparrow
fonte
0

Todas as respostas fornecidas aqui presumem (como indica o exemplo da pergunta) que não há atributos na tag. Se a resposta aceita for executada neste:

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

se será substituído por

<pre>A</pre>

E se você quiser manter os atributos também - que é o que significaria substituir uma tag ...? Esta é a solução:

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

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

    $( this ).replaceWith( content );
} );
patrick
fonte
Eu sei que isso tem alguns anos, mas eu apenas pensei em mencionar ... você se esqueceu de manter o html do elemento anterior. Em seu snippet de código, você está apenas criando um novo elemento com os atributos sem copiar nenhum dos filhos também.
Um amigo
isso seria feito adicionandocontent.html( this.html )
patrick