JavaScript function to change an element tag

Posted on

Problem

I wrote this JavaScript function to change an element tag name to a div.

function divilize(elem) {
    var elemDiv = document.createElement("div");
    var elemAttrs = elem.attributes;
    for (var attr = 0; attr < elemAttrs.length; attr++)
        elemDiv.setAttribute(elemAttrs[attr].name, elemAttrs[attr].value);
    elemDiv.className += 'divilize-'+elem.tagName.toLowerCase();
    while (elem.firstChild)
        elemDiv.appendChild(elem.firstChild);
    elem.parentNode.replaceChild(elemDiv,elem)
}

divilize(document.getElementById('one'));

Basically, it receives an element, creates a div, copies the attributes, adds in a class with the original tag name for styling purposes (I can make a .divilize-span {display: inline;}), and copies over all of the child elements. I designed with fast reusable code in mind and to avoid losing any event handlers.

Let me know your thoughts…

Solution

You will have a css concatenation issue if the passed element already has a class, for example :

<div class="test" id="one"></div>

will turn into :

<div class="testdivilize-span" id="one"></div>

Then on the actual efficiency of the function, I would use standard calls and quicker logic to speed things up :

function divilize(elm) {

   var clone = elm.cloneNode(true);
   var tagName = clone.tagName.toLowerCase(); 

   clone.className += " divilize-"+tagName; 
   elm.outerHTML = clone.outerHTML.replace(tagName, "div");   

}  

@ Fiddle

You don’t specify for what purpose you’re using this function. But if this should work generically, I would address two issues:

Valid HTML

With the current solution, you tend to produce invalid HTML or even unexpected behavior. Consider these examples:

<ul>
    <li id="target">1</li>
</ul>

<form id="target">
    <input type="text">
</form>

<input type="text" id="target">
  1. Will create a div as a direct child of an ul, which is invalid.
  2. Will remove the form-element and create orphan form fields.
  3. Removes a GUI element completely.

To address that, you can create a list of elements that can’t be converted, like:

var invalidNodeNames = ['UL', 'LI', 'FORM', 'INPUT', 'TD', 'IMG', …];

And test for these:

if (-1 != invalidNodeNames.indexOf(elem.nodeName)) {
    return;
}

Valid attributes

Actually a div-element can only have Global attributes. Furthermore notes on invalid documents say:

If a user agent encounters an attribute it does not recognize, it should ignore the entire attribute specification (i.e., the attribute and its value).

But this doesn’t mean, that an invalid attribute will have:

  • a CSS meaning [type="checkbox"] { border: 1px solid red; }
  • a meaning and behavior in the feature, when it becomes a valid attribute.

That being said, it doesn’t do much harm, but you could cleanup invalid attributes. If you think you need them at some point in the future, you can still add them as valid data-*-attributes. For example:

var validAttributes = ['accesskey', 'class', 'contenteditable', …];

if (-1 != validAttributes.indexOf(attribute.name) ||
     0 == attribute.name.indexOf('data-') ||
     0 == attribute.name.indexOf('aria-') {
    element.setAttribute(attribute.name, attribute.value);
} else {
    element.setAttribute('data-divilized-' + attribute.name, attribute.value);
}

Which results in:

<blockquote style="color: red;" cite="http://www.w3.org"></blockquote>

<div style="color: red;" data-divilized-cite="http://www.w3.org"></div>

Leave a Reply

Your email address will not be published. Required fields are marked *