115

Goal:

Using jQuery, I'm trying to replace all the occurrences of:

<code> ... </code>

with:

<pre> ... </pre>

My solution:

I got as far as the following,

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

The problem with my solution:

but the issues is that it's replacing everything between the (second, third, fourth, etc)"code" tags with the content between the first "code" tags.

e.g.

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

becomes

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

I think I need to use "this" and some sort of function but I'm afraid I'm still learning and don't really understand how to piece a solution together.

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
jon
  • 5,961
  • 8
  • 35
  • 43

13 Answers13

174

You can pass a function to .replaceWith [docs]:

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

Inside the function, this refers to the currently processed code element.

DEMO

Update: There is no big performance difference, but in case the code elements have other HTML children, appending the children instead of serializing them feels to be more correct:

$('code').replaceWith(function(){
    return $("<pre />").append($(this).contents());
});
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 2
    this seems to be the most elegant solution, tho i'll admit i don't understand what's going on inside the replaceWith function. would love to hear more. i.e. how is assigning the opening AND closing tags? does the space in
     accomplish that?
    – jon Aug 17 '11 at 13:32
  • 2
    @jon: It is a short hand for creating new elements. More information can be found here: http://api.jquery.com/jQuery/#jQuery2. jQuery loops over all the elements and executes the function for each of them (same what `.each` is doing). – Felix Kling Aug 17 '11 at 13:33
  • @jon: But check Jens' solution again, he fixed it. – Felix Kling Aug 17 '11 at 13:34
  • 1
    +1 for a nice trick - this can be used for a lot more than just simple tag-for-tag substitution. – Jens Roland Aug 17 '11 at 14:02
  • 1
    @FelixKling Wouldn't this overwrite the attributes of the code tags? – tewathia Dec 10 '13 at 14:53
  • @tewathia: Yes. If you want to preserve the attributes, have a look at [this question](http://stackoverflow.com/q/8584098/218196). – Felix Kling Dec 10 '13 at 15:53
  • Yeah, I have modified your code slightly to include that as well. [Here](http://stackoverflow.com/questions/7093417/using-jquery-to-replace-one-tag-with-another/7093472?noredirect=1#comment30640487_7093472) – tewathia Dec 10 '13 at 16:06
85

This is much nicer:

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

Though admittedly Felix Kling's solution is approximately twice as fast:

Community
  • 1
  • 1
Jens Roland
  • 27,450
  • 14
  • 82
  • 104
  • 5
    worked like a charm, to my lay eyes this seems to be the most efficient solution. :) – jon Aug 17 '11 at 13:26
  • aha. yea, you guys were right. it didn't work for me either. thanks for catching that! – jon Aug 17 '11 at 13:28
  • 1
    FWIW, `$('code').children()` will return an empty jQuery object for the example above, because the `code` elements have no child element nodes. Though it works *if* there are child elements. – Felix Kling Aug 17 '11 at 13:30
  • 1
    Fixed - true, when the contents consist of a single text node, `children()` won't work. Using `contents()` instead works perfectly. http://jsfiddle.net/7Yne9/ – Jens Roland Aug 17 '11 at 13:31
  • as should be clear by now. i'm a bit lost in all this but you kind fellows seem to think this is the best solution and now that it's working i'll pass along what appreciate i can: my vote. thanks for your help everyone! – jon Aug 17 '11 at 13:38
  • 1
    Here is a jsperf version which does not break the page: http://jsperf.com/substituting-one-tag-for-another-with-jquery/2 and you see the speed difference is not that huge anymore. Your solution is slower because you perform two DOM manipulations per element: `unwrap` removes the parent and adds the children to the tree, `wrap` detaches them again and adds a new parent. – Felix Kling Aug 22 '11 at 23:55
  • *Update:* Here is again a better jsperf: http://jsperf.com/substituting-one-tag-for-another-with-jquery/4 I think you have to reset the DOM every time you run the a test (otherwise there is nothing to do for the other iterations). – Felix Kling Aug 23 '11 at 00:28
  • @Felix: thanks for the corrections - the performance hit is quite serious there. Too bad - I really liked the wrap-unwrap syntax. Although this is hardly a function which will be run in a loop or on huge pages of `` tags, so performance may not be a big issue – Jens Roland Aug 23 '11 at 07:02
  • @Jens: Exactly. Making benchmarks is good. But if there is no performance problem, then there is nothing to worry :) – Felix Kling Aug 23 '11 at 08:42
28

It's correct that you'll always obtain the first code's contents, because $('code').html() will always refer to the first element, wherever you use it.

Instead, you could use .each to iterate over all elements and change each one individually:

$('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
  • 151,816
  • 78
  • 307
  • 352
13

Try this:

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

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

});

http://jsfiddle.net/mTGhV/

Kokos
  • 9,051
  • 5
  • 27
  • 44
  • wow. you guys came up with the exact same solution within 2 seconds of each other. there are tiny formatting differences. I'm not sure which one to upvote - I really don't like the space between function and () in thomas' answer and the 2 lines of empty space in kokos answer are pretty pointlness + there should be a space between the () and the { – Michael Bylstra Feb 13 '13 at 07:17
13

How about this?

$('code').each(function () {
    $(this).replaceWith( "<pre>" + $(this).html() + "</pre>" );
});
Xavi
  • 20,111
  • 14
  • 72
  • 63
Tae-Sung Shin
  • 20,215
  • 33
  • 138
  • 240
7

Building up on Felix's answer.

$('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;
});

This will reproduce the attributes of the code tags in the replacement pre tags.

Edit: This will replace even those code tags that are inside the innerHTML of other code tags.

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
  • 6,890
  • 3
  • 22
  • 27
  • 2
    best answer so far. The other ones are pretty much all the same which is really odd that people upvote them. Congratz for being the only one that thought about the attributes. – Guilherme David da Costa Dec 22 '13 at 22:56
3

As of jQuery 1.4.2:

$('code').replaceWith(function(i,html) {
  return $('<pre />').html(html);
});​

You can then select the new elements:

$('pre').css('color','red');

Source: http://api.jquery.com/replaceWith/#comment-45493689

jsFiddle: http://jsfiddle.net/k2swf/16/

Simon
  • 31
  • 2
2

If you were using vanilla JavaScript you would:

  • Create the new element
  • Move the children of old element into the new element
  • Insert the new element before the old one
  • Remove the old element

Here is jQuery equivalent of this process:

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

Here is the jsperf URL:
http://jsperf.com/substituting-one-tag-for-another-with-jquery/7

PS: All solutions that use .html() or .innerHTML are destructive.

Salman A
  • 262,204
  • 82
  • 430
  • 521
2

Another short & easy way:

$('code').wrapInner('<pre />').contents();
Fabian von Ellerts
  • 4,763
  • 40
  • 35
1

All answers given here assume (as the question example indicates) that there are no attributes in the tag. If the accepted answer is ran on this:

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

if will be replaced with

<pre>A</pre>

What if you want to keep the attributes as well - which is what replacing a tag would mean... ? This is the solution:

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

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

    $( this ).replaceWith( content );
} );
patrick
  • 11,519
  • 8
  • 71
  • 80
  • I know this is a couple of years old but I just thought I'd mention... you forgot to keep the html from the previous element. In your code snippet you're just creating a new element with the attributes without copying any of the children over as well. – A Friend May 19 '20 at 16:51
  • that would be done by adding `content.html( this.html )` – patrick May 19 '20 at 19:50
0
$('code').each(function(){
    $(this).replaceWith( "<pre>" + $(this).html()  + "</pre>" );
    });

Best and clean way.

Nimek
  • 1
0

You could use jQuery's html function. Below is a sample the replaces a code tag with a pre tag while retaining all of the attributes of the code.

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

This could work with any set of html element tags that needed to be swapped while retaining all the attributes of the previous tag.

ControlAltDelete
  • 3,576
  • 5
  • 32
  • 50
0

Made jquery plugin, maintaining attributes also.

$.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')
Jagdish Idhate
  • 7,513
  • 9
  • 35
  • 51