71

jQuery: how to change tag name?

For example:

<tr>
    $1
</tr>

I need

<div>
    $1
</div>

Yes, I can

  1. Create DOM element <div>
  2. Copy tr content to div
  3. Remove tr from dom

But can I make it directly?

PS:

    $(tr).get(0).tagName = "div"; 

results in DOMException.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
puchu
  • 3,294
  • 6
  • 38
  • 62
  • 5
    In this special case, it would not make sense to just "rename" it because `div` won't be a valid element where `tr` is located. – Felix Kling Aug 08 '10 at 20:11
  • See this post for a more complete solution that includes all attributes: http://stackoverflow.com/questions/2815683/jquery-javascript-replace-tag-type – Grinn Jul 17 '12 at 15:47
  • 1
    you can also use `display:block` to make a `tr` act and look like a `div` without actually changing the tag name but you would probably want to `display:block`ify the inner `td`s as well with something like `tr,tr>td{display:block}`. – oriadam Jan 24 '21 at 09:09

17 Answers17

57

You can replace any HTML markup by using jQuery's .replaceWith() method.

example: http://jsfiddle.net/JHmaV/

Ref.: .replaceWith

If you want to keep the existing markup, you could use code like this:

$('#target').replaceWith('<newTag>' + $('#target').html() +'</newTag>')
Accountant م
  • 6,975
  • 3
  • 41
  • 61
jAndy
  • 231,737
  • 57
  • 305
  • 359
  • 19
    This will work, but you won't carry over the dom element's properties (styles, events) etc. I don't think there exists a good way to really achieve a full node name change. – Jason Apr 06 '11 at 20:06
  • 10
    Sorry, it is NOT a "rename", it destroy all contents (all innerHTML changes!). – Peter Krauss Feb 12 '14 at 10:50
  • 1
    it is a replace, not rename! – Ari Jun 19 '15 at 12:04
  • 7
    Well to keep events etc of the contents, one could have `.replaceWith($('').append($('#target').contents())` –  Jul 18 '16 at 14:19
47

No, it is not possible according to W3C specification: "tagName of type DOMString, readonly"

http://www.w3.org/TR/DOM-Level-2-Core/core.html

ggarber
  • 8,300
  • 5
  • 27
  • 32
  • I thing que puchu's question is only about "rename procedure" (!), and there are a "DOM ugly way" to do rename: 1) `createElement(new_name)` 2) copy all content to new element; 3) replace old to new by `replaceChild()` – Peter Krauss Feb 12 '14 at 10:55
17

The above solutions wipe out the existing element and re-create it from scratch, destroying any event bindings on children in the process.

short answer: (loses <p/>'s attributes)

$("p").wrapInner("<div/>").children(0).unwrap();

longer answer: (copies <p/>'s attributes)

$("p").each(function (o, elt) {
  var newElt = $("<div class='p'/>");
  Array.prototype.slice.call(elt.attributes).forEach(function(a) {
    newElt.attr(a.name, a.value);
  });
  $(elt).wrapInner(newElt).children(0).unwrap();
});

fiddle with nested bindings

It would be cool to copy any bindings from the at the same time, but getting current bindings didn't work for me.

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
ericP
  • 1,675
  • 19
  • 21
16

Where the DOM renameNode() Method?

Today (2014) no browser understand the new DOM3 renameNode method (see also W3C) check if run at your bowser: http://jsfiddle.net/k2jSm/1/

So, a DOM solution is ugly and I not understand why (??) jQuery not implemented a workaround?

pure DOM algorithm

  1. createElement(new_name)
  2. copy all content to new element;
  3. replace old to new by replaceChild()

is something like this,

function rename_element(node,name) {
    var renamed = document.createElement(name); 
    foreach (node.attributes as a) {
        renamed.setAttribute(a.nodeName, a.nodeValue);
    }
    while (node.firstChild) {
        renamed.appendChild(node.firstChild);
    }
    return node.parentNode.replaceChild(renamed, node);
}

... wait review and jsfiddle ...

jQuery algorithm

The @ilpoldo algorithm is a good start point,

   $from.replaceWith($('<'+newname+'/>').html($from.html()));

As others commented, it need a attribute copy ... wait generic ...

specific for class, preserving the attribute, see http://jsfiddle.net/cDgpS/

See also https://stackoverflow.com/a/9468280/287948

Community
  • 1
  • 1
Peter Krauss
  • 13,174
  • 24
  • 167
  • 304
7

To preserve the internal content of the tag you can use the accessor .html() in conjunction with .replaceWith()

forked example: http://jsfiddle.net/WVb2Q/1/

ilpoldo
  • 506
  • 6
  • 7
  • 2
    how about saving all attributes of any element? –  May 16 '11 at 06:56
  • just what i was searching for +1. Attributes are not included with other solutions, until now, this one is a better one – I.G. Pascual May 26 '11 at 13:06
  • No, it doesn't preserve the attributes. – Grinn Jul 17 '12 at 15:41
  • Yes... But it is in the right direction! It is a "rename procedure"... Complement it with a attribute-copy, http://stackoverflow.com/a/6753486/287948, or using [clone](http://api.jquery.com/clone/). – Peter Krauss Feb 12 '14 at 11:13
7

Inspired by ericP answer, formatted and converted to jQuery plugin:

$.fn.replaceWithTag = function(tagName) {
    var result = [];
    this.each(function() {
        var newElem = $('<' + tagName + '>').get(0);
        for (var i = 0; i < this.attributes.length; i++) {
            newElem.setAttribute(
                this.attributes[i].name, this.attributes[i].value
            );
        }
        newElem = $(this).wrapInner(newElem).children(0).unwrap().get(0);
        result.push(newElem);
    });
    return $(result);
};

Usage:

$('div').replaceWithTag('span')
Dmitriy Sintsov
  • 3,821
  • 32
  • 20
5

Working pure DOM algorithm

function rename_element(node, name) {
    let renamed = document.createElement(name);

    Array.from(node.attributes).forEach(attr => {
        renamed.setAttribute(attr.name, attr.value);        
    })
    while (node.firstChild) {
        renamed.appendChild(node.firstChild);
    }
    node.parentNode.replaceChild(renamed, node);
    return renamed;
}

  • Pretty modern code, and works. Usage example: `$('blockquote').each(function(){let ren = rename_element(this, 'div'); $(ren).doMoreJqueryStuff()})` – Nils Lindemann Apr 02 '21 at 22:18
2

You could go a little basic. Works for me.

var oNode = document.getElementsByTagName('tr')[0];

var inHTML = oNode.innerHTML;
oNode.innerHTML = '';
var outHTML = oNode.outerHTML;
outHTML = outHTML.replace(/tr/g, 'div');
oNode.outerHTML = outHTML;
oNode.innerHTML = inHTML;
Cory
  • 113
  • 9
1

JS to change the tag name

/**
 * This function replaces the DOM elements's tag name with you desire
 * Example:
 *        replaceElem('header','ram');
 *        replaceElem('div.header-one','ram');
 */
function replaceElem(targetId, replaceWith){
  $(targetId).each(function(){
    var attributes = concatHashToString(this.attributes);
    var replacingStartTag = '<' + replaceWith + attributes +'>';
    var replacingEndTag = '</' + replaceWith + '>';
    $(this).replaceWith(replacingStartTag + $(this).html() + replacingEndTag);
  });
}
replaceElem('div','span');

/**
 * This function concats the attributes of old elements
 */
function concatHashToString(hash){
  var emptyStr = '';
  $.each(hash, function(index){
    emptyStr += ' ' + hash[index].name + '="' + hash[index].value + '"';
  });
  return emptyStr;
}

Related fiddle is in this link

Shiva
  • 11,485
  • 2
  • 67
  • 84
1

To replace the internal contents of multiple tags, each with their own original content, you have to use .replaceWith() and .html() differently:

http://jsfiddle.net/kcrca/VYxxG/

user856027
  • 19
  • 1
0

Since replaceWith() didn't work for me on an element basis (maybe because I used it inside map()), I did it by creating a new element and copying the attributes as needed.

$items = $('select option').map(function(){

  var
    $source = $(this),
    $copy = $('<li></li>'),
    title = $source.text().replace( /this/, 'that' );

  $copy
    .data( 'additional_info' , $source.val() )
    .text(title);

  return $copy;
});

$('ul').append($items);
WoodrowShigeru
  • 1,418
  • 1
  • 18
  • 25
0

Take him by the word

Taken the Question by Word "how to change tag name?" I would suggest this solution:
If it makes sense or not has to be decided case by case.

My example will "rename" all a-Tags with hyperlinks for SMS with span tags. Maintaining all attributes and content:

$('a[href^="sms:"]').each(function(){
  var $t=$(this);
  var $new=$($t.wrap('<div>')
    .parent()
        .html()
        .replace(/^\s*<\s*a/g,'<span')
        .replace(/a\s*>\s*$/g,'span>')
        ).attr('href', null);
  $t.unwrap().replaceWith($new);
});

As it does not make any sense to have a span tag with an href attribute I remove that too. Doing it this way is bulletproof and compatible with all browsers that are supported by jquery. There are other ways people try to copy all the Attributes to the new Element, but those are not compatible with all browsers.

Although I think it is quite expensive to do it this way.

0

Jquery plugin to make "tagName" editable :

(function($){
    var $newTag = null;
    $.fn.tagName = function(newTag){
        this.each(function(i, el){
            var $el = $(el);
            $newTag = $("<" + newTag + ">");

            // attributes
            $.each(el.attributes, function(i, attribute){
                $newTag.attr(attribute.nodeName, attribute.nodeValue);
            });
            // content
            $newTag.html($el.html());

            $el.replaceWith($newTag);
        });
        return $newTag;
    };
})(jQuery);

See : http://jsfiddle.net/03gcnx9v/3/

cedrik
  • 541
  • 7
  • 17
0

Yet another script to change the node name

function switchElement() {
  $element.each(function (index, oldElement) {
    let $newElement = $('<' + nodeName + '/>');
    _.each($element[0].attributes, function(attribute) {
      $newElement.attr(attribute.name, attribute.value);
    });
    $element.wrapInner($newElement).children().first().unwrap();
  });
}

http://jsfiddle.net/rc296owo/5/

It will copy over the attributes and inner html into a new element and then replace the old one.

Berty
  • 1,081
  • 1
  • 18
  • 49
0

$(function(){
    $('#switch').bind('click', function(){
        $('p').each(function(){
         $(this).replaceWith($('<div/>').html($(this).html()));
        });
    });
});
p {
    background-color: red;
}

div {
    background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Hello</p>
<p>Hello2</p>
<p>Hello3</p>
<button id="switch">replace</button>
Ifeanyi Chukwu
  • 3,187
  • 3
  • 28
  • 32
0

You can use this function

var renameTag  = function renameTag($obj, new_tag) {
    var obj = $obj.get(0);
    var tag = obj.tagName.toLowerCase();
    var tag_start = new RegExp('^<' + tag);
    var tag_end = new RegExp('<\\/' + tag + '>$');
    var new_html = obj.outerHTML.replace(tag_start, "<" + new_tag).replace(tag_end, '</' + new_tag + '>');
    $obj.replaceWith(new_html);
};

ES6

const renameTag = function ($obj, new_tag) {
    let obj = $obj.get(0);
    let tag = obj.tagName.toLowerCase();
    let tag_start = new RegExp('^<' + tag);
    let tag_end = new RegExp('<\\/' + tag + '>$');
    let new_html = obj.outerHTML.replace(tag_start, "<" + new_tag).replace(tag_end, '</' + new_tag + '>');
    $obj.replaceWith(new_html);
};

Sample code

renameTag($(tr),'div');
Sarath Ak
  • 7,903
  • 2
  • 47
  • 48
0

Try this one also. in this example we can also have attributes of the old tag in new tag

var newName = document.querySelector('.test').outerHTML.replaceAll('h1', 'h2');
document.querySelector('.test').outerHTML = newName;
<h1 class="test">Replace H1 to H2</h1>
AbdulAhmad Matin
  • 1,047
  • 1
  • 18
  • 27