4

Is it possible to replace a nodes name? Like:

HTML:

<strong id="element">Text</strong>

Javascript:

var element = document.getElementById("element");
    element.nodeName = "b";

As I see it's not working, if it's not possible in this way, then how can it be done?

Why I need it:

I'm building a Text Editor, and IE uses strong instead of b in the execCommand() function and I would like to change that, I tried to build the execCommand("bold") from scratch but there is a lots of problem and differences even between IE 8 and 9. So now I decided to change it's node name, it would be really easy, but doesn't works.. :(

Note: I need this to work only in Internet Explorer.

Thanks

Adam Halasz
  • 57,421
  • 66
  • 149
  • 213
  • 2
    What are you trying to achieve? Why is it important for you that your STRONG element has a `'B'` name? – Šime Vidas Mar 26 '11 at 17:47
  • I'm building a Text Editor, and IE uses `strong` instead of `b` in the `execCommand()` function and I would like to change that, I tried to build the `execCommand("bold")` from scratch but there is a lots of problem and differences even between IE 8 and 9. So now I decided to change it's node name, it would be really easy, but doesn't works.. :( – Adam Halasz Mar 26 '11 at 17:50
  • I'm working on a replacement for `document.execCommand()` module for my Rangy library. I have a working bold example now, although it only uses `` at the moment. http://rangy.googlecode.com/svn/trunk/demos/bold.html – Tim Down Mar 27 '11 at 01:48

5 Answers5

5

No, but you can replace the node easily:

var oldNode = document.getElementById('element'),
    newNode = document.createElement('b'),
    node,
    nextNode;

node = oldNode.firstChild;
while (node) {
    nextNode = node.nextSibling;
    newNode.appendChild(node);
    node = nextNode;
}

newNode.className = oldNode.className;
// Do attributes too if you need to
newNode.id = oldNode.id; // (Not invalid, they're not both in the tree at the same time)
oldNode.parentNode.replaceChild(newNode, oldNode);

Live example

Many thanks to Haochi for pointing out replaceChild, I had done this:

oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

Live example ...but replaceChild is cleaner.

Docs:

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Use [replaceChild](https://developer.mozilla.org/En/DOM/Node.replaceChild) instead of `insertBefore` & `removeChild`. – HChen Mar 26 '11 at 17:52
  • I have my own replace function too and works great, but It's a good idea to replace the node. – Adam Halasz Mar 26 '11 at 17:56
3
Element.prototype.setTagName=function(strTN) {
 var oHTML=this.outerHTML, tempTag=document.createElement(strTN); //document.createElement will fire an error if string has wrong characters.
 var tName={original: this.tagName.toUpperCase(), change: strTN.toUpperCase()}
 if (tName.original == tName.change) return;
 oHTML=oHTML.replace(RegExp("(^\<" + tName.original + ")|(" + tName.original + "\>$)","gi"), function(x){return (x.toUpperCase().replace(tName.original, tName.change));});
 tempTag.innerHTML=oHTML;
 this.parentElement.replaceChild(tempTag.firstChild,this);
}

Use (in case you want to set a span for body's first element):

document.body.firstElementChild.setTagName("SPAN");

EDIT:

I forgot to mention something. It creates a new element so if you have the original one stored in a variable, the variable will be storing the old (unchanged) one. If the Element doesn't have a parent Element it fails.

That's why I made a new one:

Element.prototype.spawn = function(strTN = "spawn") {
 let tName = ({original: this.tagName.toUpperCase(), change: strTN.toUpperCase()}); if (tName.original == tName.change) { return; }
 let fragment = document.createRange().createContextualFragment(this.outerHTML.replace(new RegExp("(^\<\\b" + tName.original + "\\b)|(\\b" + tName.original + "\\b\>$)","gi"), function(x){return(x.toUpperCase().replace(tName.original, tName.change));}));
 return(fragment.firstChild);
}

This one just creates the new Element with all the original descendants but you have to place it to the DOM manually if you want:

var e = document.body.firstElementChild;
e.replaceWith(e.spawn("i"));
t33st33r
  • 197
  • 2
  • 9
2

You could try getting the outerHTML and replacing the start and end tags.

var element = document.getElementById("element");
element.outerHTML = element.outerHTML.trim()
                                     .replace('<strong ','<button ')
                                     .replace('</strong>'.'</button');

Note well though, the solution above is applicable only for simple use cases. For a better solution go through snippet below.

var element = document.getElementById("element");
changeNodeName(element,'button');

function changeNodeName(el,str){
  var elNodeName = el.nodeName.toLowerCase();
  var newString = el.outerHTML.trim()
                    .replace('<'+ elNodeName,'<'+str);

  // To replace the end tag, we can't simply use replace()
  // because, replace() will replace the first occurrence,
  // which means if our element has a child element with the
  // same node name the end tag of the *child element* will be 
  // replaced, not the parent element. So,

  newString = newString
           .slice(0,newString.lastIndexOf('</'+str+'>'));    
  //now newString = "<button id='element'>Text"

  newString = newString + "</" + str + ">";
  el.outerHTML = newString;
}
<strong id="element">
  <strong>
    Text
  </strong>
</strong>
Tejasvi Karne
  • 628
  • 4
  • 14
1

No, nodeName is read-only. From the spec:

readonly attribute DOMString       nodeName;

See here: http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1950641247

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
0

You could store the innerHTML value of the strong element in a temp variable, then create a new "b" element and set its innerHTML to the value stored in the temp variable, and finally use the replaceChild method on the strong element's parent to replace the strong element with the new b element.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255