3

For example I have this HTML:

<body>
    <div>Text</div>
</body>

And I would like to change the div to something else like p.

This is what I have tried but doesn't works:

var div = document.getElementsByTagName("div")[0]; // Get Element
    div.nodeName = "p"; // Change It's Node Name to P

Please no libraries, and I don't really want to replace the actual div with a new p :)

PeeHaa
  • 66,697
  • 53
  • 182
  • 254
Adam Halasz
  • 51,803
  • 63
  • 138
  • 208

5 Answers5

9

You cannot just change an element. You have to create a new one. E.g.:

var div = document.getElementsByTagName("div")[0];
var p = document.createElement('p');
p.innerHTML = div.innerHTML;
div.parentNode.replaceChild(p, div);

But this could lead to invalid markup, if the original element contains nodes that cannot be descendants of the new node.

Reference: document.createElement, Node.replaceChild


Note: A better version (because it doesn't depend on serializing DOM to text and back and preserves attributes), can be found at https://stackoverflow.com/a/8584158/218196 .

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
  • Well, I did this too, but isn't there any pre defined javascript functions or something? :) – Adam Halasz Feb 14 '11 at 23:11
  • I've got the answer for my comment from the others too, if Mozzila says, than probably this is the best way to do it :) – Adam Halasz Feb 14 '11 at 23:14
  • Doesn't copy attributes! – wybe Apr 23 '18 at 22:28
  • @wybe: That's correct. Here is a better version: https://stackoverflow.com/a/8584158/218196 – Felix Kling Apr 24 '18 at 06:33
  • *Never use `innerHTML`!* Not only is it a proprietary Microsoft method it does *not* register the HTML in the DOM in many instances. That means you can not reference an `id` attribute with JavaScript afterwards in some browsers. – John Dec 24 '20 at 16:55
  • 1
    @John: What's your source? Which browsers cause problems? – Felix Kling Dec 24 '20 at 21:18
  • @John That is demonstrably not true. I just did it in the console on this very page. – Chris Baker Dec 25 '20 at 01:30
  • You're testing browsers, and likely only Chrome. You need to test *engines* and there are many and then many (mostly pointless) versions of each. Vast majority of devs go from one project to the next, they may have a wide understanding though not a deep. Since I've blacklisted `innerHTML` for nearly two decades you will likely have to go back a ways if it seems to work for you. Import to an XHTML application and reference the `id` attribute without using things like `importNode`. Easy is cheap and a cheap foundation does not create a long-lasting anything. – John Dec 25 '20 at 11:29
  • @John Not to get into too great a debate, but you were asked for a source or evidence of this claim by Felix. I, personally, have not encountered the issues you've claimed on any engine in the wild. Ever. I have tested plenty of code on plenty of engines and platforms, always seeking the engine support required, per market share, for any commercially viable product I've ever worked on. So I would, from a professional standpoint, be QUITE interested in evidence of your extraordinary claim. – Chris Baker Dec 30 '20 at 02:48
  • @ChrisBaker "from a professional standpoint" I will make the presumption that you're a web developer and I will make the second presumption that you remember the days of IE and how actual professionals fought for standards compliance. To disregard `innerHTML` as a proprietary Microsoft method and instead of seeking proper standards-compliant methods to solve the problems you're calling my "claim" extraordinary? I think you've seriously lost perspective here. – John Dec 30 '20 at 10:48
  • 1
    @John: Maybe it was proprietary at some point, but doesn't look like it still is: https://html.spec.whatwg.org/#dom-innerhtml, https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin . – Felix Kling Dec 30 '20 at 11:06
  • @FelixKling `nodeValue`, `textContent`, `importNode`, `appendChild` & a plethora of *actually* standard methods have long existed and *do not have issues*. If you really want to go digging for the problem try browsers from the mid-2000s, AJAX some XML to an XHTML DOM using `innerHTML` and then reference an imported `id` attribute. If you do it correctly you will find that it fails. I literally supersede standards bodies and browsers that can't even competently handle simple tasks such as `getComputedStyle` on a near monthly basis & I will not reduce my standards because others degrade theirs. – John Dec 30 '20 at 11:53
  • @John It's been available in almost every browser in some form or another, forever. `document.getElementById('someid')` works against AJAX'd HTML. I don't know what you mean by "imported", but I've definitely referenced dynamic HTML. I think your perception of "degraded" standards is borne of a fundamental misunderstanding of the language and its implementation. I don't know what you mean by "I supersede standards bodies and browsers", but you're just wrong about this whole thing. `innerHTML` is a tool that works just fine, if used correctly. – Chris Baker Jan 02 '21 at 07:46
1

You can't.

As the MDC docs say:

nodeName is a read-only attribute.

You'll have to create a new element and give it the right content and attributes.

lonesomeday
  • 215,182
  • 48
  • 300
  • 305
1

The reason you can't just change the tagName property is because different HTML tags are actually different classes of objects. A div tag is an HTMLDivElement instance, a p tag is an HTMLParagraphElement instance, and so on. These classes can have vastly different properties and interfaces, so turning one into another is not as trivial as you'd think.

Máté Safranka
  • 3,821
  • 1
  • 8
  • 21
0

You cannot. The propery you're after is tagName, but it is read only. You would instead have to create a new node of the desired type, then transfer the innerHTML (and any other properties like className or style) to the new node. Then, insert the new node into the old node's parent, then remove the old node (or use replaceChild).

In other words, the long road is the only road.

Chris Baker
  • 46,402
  • 12
  • 93
  • 112
0

I solved this in an XML scenario (eg. where there is no innerHTML) like so:

function renameNode (node, newNodeName) {
    const newNode = node.ownerDocument.createElement(newNodeName);
    Array.from(node.attributes).forEach(attr => newNode.setAttribute(attr.localName, attr.value));
    Array.from(node.childNodes).forEach(childNode => newNode.appendChild(childNode));

    node.parentElement.insertBefore(newNode, node);
    node.parentElement.removeChild(node);
}

Does not return anything, but will update your DOM.

wybe
  • 447
  • 4
  • 13