Advantages of createElement over innerHTML?

39,443

Solution 1

There are several advantages to using createElement instead of modifying innerHTML (as opposed to just throwing away what's already there and replacing it) besides safety, like Pekka already mentioned:

Preserves existing references to DOM elements when appending elements

When you append to (or otherwise modify) innerHTML, all the DOM nodes inside that element have to be re-parsed and recreated. If you saved any references to nodes, they will be essentially useless, because they aren't the ones that show up anymore.

Preserves event handlers attached to any DOM elements

This is really just a special case (although common) of the last one. Setting innerHTML will not automatically reattach event handlers to the new elements it creates, so you would have to keep track of them yourself and add them manually. Event delegation can eliminate this problem in some cases.

Could be simpler/faster in some cases

If you are doing lots of additions, you definitely don't want to keep resetting innerHTML because, although faster for simple changes, repeatedly re-parsing and creating elements would be slower. The way to get around that is to build up the HTML in a string and set innerHTML once when you are done. Depending on the situation, the string manipulation could be slower than just creating elements and appending them.

Additionally, the string manipulation code may be more complicated (especially if you want it to be safe).

Here's a function I use sometimes that make it more convenient to use createElement.

function isArray(a) {
    return Object.prototype.toString.call(a) === "[object Array]";
}

function make(desc) {
    if (!isArray(desc)) {
        return make.call(this, Array.prototype.slice.call(arguments));
    }

    var name = desc[0];
    var attributes = desc[1];

    var el = document.createElement(name);

    var start = 1;
    if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
        for (var attr in attributes) {
            el[attr] = attributes[attr];
        }
        start = 2;
    }

    for (var i = start; i < desc.length; i++) {
        if (isArray(desc[i])) {
            el.appendChild(make(desc[i]));
        }
        else {
            el.appendChild(document.createTextNode(desc[i]));
        }
    }

    return el;
}

If you call it like this:

make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);

you get the equivalent of this HTML:

<p>Here is a <a href="http://www.google.com/">link</a>.</p>

Solution 2

User bobince puts a number of cons very, very well in his critique of jQuery.

... Plus, you can make a div by saying $(''+message+'') instead of having to muck around with document.createElement('div') and text nodes. Hooray! Only... hang on. You've not escaped that HTML, and have probably just created a cross-site-scripting security hole, only on the client side this time. And after you'd spent so long cleaning up your PHP to use htmlspecialchars on the server-side, too. What a shame. Ah well, no-one really cares about correctness or security, do they?

jQuery's not wholly to blame for this. After all, the innerHTML property has been about for years, and already proved more popular than DOM. But the library certainly does encourage that style of coding.

As for performance: InnerHTML is most definitely going to be slower, because it needs to be parsed and internally converted into DOM elements (maybe using the createElement method).

InnerHTML is faster in all browsers according to the quirksmode benchmark provided by @Pointy.

As for readability and ease of use, you will find me choosing innerHTML over createElement any day of the week in most projects. But as you can see, there are many points speaking for createElement.

Solution 3

While innerHTML may be faster, I don't agree that it is better in terms of readability or maintenance. It may be shorter to put everything in one string, but shorter code is not always necessarily more maintainable.

String concatenation just does not scale when dynamic DOM elements need to be created as the plus' and quote openings and closings becomes difficult to track. Consider these examples:

The resulting element is a div with two inner spans whose content is dynamic. One of the class names (warrior) inside the first span is also dynamic.

<div>
    <span class="person warrior">John Doe</span>
    <span class="time">30th May, 2010</span>
</div>

Assume the following variables are already defined:

var personClass = 'warrior';
var personName = 'John Doe';
var date = '30th May, 2010';

Using just innerHTML and mashing everything into a single string, we get:

someElement.innerHTML = "<div><span class='person " + personClass + "'>" + personName + "</span><span class='time'>" + date + "</span></div>";

The above mess can be cleaned up with using string replacements to avoid opening and closing strings every time. Even for simple text replacements, I prefer using replace instead of string concatenation.

This is a simple function that takes an object of keys and replacement values and replaces them in the string. It assumes the keys are prefixed with $ to denote they are a special value. It does not do any escaping or handle edge cases where $ appears in the replacement value etc.

function replaceAll(string, map) {
    for(key in map) {
        string = string.replace("$" + key, map[key]);
    }
    return string;
}

var string = '<div><span class="person $type">$name</span><span class="time">$date</span></div>';
var html = replaceAll(string, {
    type: personClass,
    name: personName,
    date: date
});
someElement.innerHTML = html;

​This can be improved by separating the attributes, text, etc. while constructing the object to get more programmatic control over the element construction. For example, with MooTools we can pass object properties as a map. This is certainly more maintainable, and I would argue more readable as well. jQuery 1.4 uses a similar syntax to pass a map for initializing DOM objects.

var div = new Element('div');

var person = new Element('span', {
    'class': 'person ' + personClass,
    'text': personName
});

var when =  new Element('span', {
    'class': 'time',
    'text': date
});

div.adopt([person, when]);

I wouldn't call the pure DOM approach below to be any more readable than the ones above, but it's certainly more maintainable because we don't have to keep track of opening/closing quotes and numerous plus signs.

var div = document.createElement('div');

var person = document.createElement('span');
person.className = 'person ' + personClass;
person.appendChild(document.createTextNode(personName));

var when = document.createElement('span');
​when.className = 'date​​​​​​';
when.appendChild(document.createTextNode(date));

​div.appendChild(person);
div.appendChild(when);

The most readable version would most likely result from using some sort of JavaScript templating.

<div id="personTemplate">
    <span class="person <%= type %>"><%= name %></span>
    <span class="time"><%= date %></span>
</div>

var div = $("#personTemplate").create({
    name: personName,
    type: personClass,
    date: date
});

Solution 4

You should use createElement if you want to keep references in your code. InnerHTML can sometimes create a bug that is hard to spot.

HTML code:

<p id="parent">sample <span id='test'>text</span> about anything</p>

JS code:

var test = document.getElementById("test");

test.style.color = "red"; //1 - it works

document.getElementById("parent").innerHTML += "whatever";

test.style.color = "green"; //2 - oooops

1) you can change the color

2) you can't change color or whatever else anymore, because in the line above you added something to innerHTML and everything is re-created and you have access to something that doesn't exist anymore. In order to change it you have to again getElementById.

You need to remember that it also affects any events. You need to re-apply events.

InnerHTML is great, because it is faster and most time easier to read but you have to be careful and use it with caution. If you know what you are doing you will be OK.

Solution 5

Template literals (Template strings) is another option.

const container = document.getElementById("container");

const item_value = "some Value";

const item = `<div>${item_value}</div>`

container.innerHTML = item;
Share:
39,443

Related videos on Youtube

oninea
Author by

oninea

Updated on June 11, 2021

Comments

  • oninea
    oninea almost 3 years

    In practice, what are the advantages of using createElement over innerHTML? I am asking because I'm convinced that using innerHTML is more efficient in terms of performance and code readability/maintainability but my teammates have settled on using createElement as the coding approach. I just wanna understand how createElement can be more efficient.

  • Pointy
    Pointy almost 14 years
    Uhh @Pekka are you really sure about innerHTML being slower? I know that for a long time that was definitely false. Using innerHTML in fact became popular inside frameworks precisely because of the dramatic performance advantage in some browsers (guess which).
  • Pekka
    Pekka almost 14 years
    @Pointy Interesting. I have no benchmarks but common sense tells me innerHTML has to be slower: It has to be parsed, validated, and turned into DOM elements, something that createElement does in the first step. If you know any benchmarks that say otherwise, though, I'll happily stand corrected.
  • Pointy
    Pointy almost 14 years
    Well the quirksmode benchmark is a little old: quirksmode.org/dom/innerhtml.html
  • Pekka
    Pekka almost 14 years
    @Pointy still: Wow! Goes totally against what I'd have thought. Cheers, will update my answer.
  • Pointy
    Pointy almost 14 years
    I agree completely that this state of affairs makes no sense.
  • Dean Harding
    Dean Harding almost 14 years
    I assume it's because for innerHTML, the parsing and DOM-creation all happens inside the engine. It's most likely the Javascript->engine transition that's slow: for innerHTML, there's only one such transition, but for createElement and co, it's lots. That's just a guess, of course.
  • oninea
    oninea almost 14 years
    Thanks for the insight Pekka and Pointy. This reinforces my view that innerHTML is quicker (in addition to coding experience). Also, using html strings in an array provides the extra performance gain and readability as well. What I wanna know is if createElement has any points to using it .
  • Deva
    Deva almost 14 years
    There are also demonstrated speed advantages to creating a DOM tree fragment in isolation and then attaching it to the real DOM once, at the end.
  • Matthew Crumley
    Matthew Crumley almost 14 years
    @staticsan, that's a good point (and another thing the make function can make a little easier).
  • oninea
    oninea almost 14 years
    Creating a DOM fragment in isolation definitely speeds up the insertion. With regards to innerHTML, the same approach can/should be used too. I usually push my HTML strings to an array then join the elements using the built in array join function (quickest in my experience) then append this to the DOM.
  • oninea
    oninea almost 14 years
    @Matthew Crumley: IYO, would you think the advantages you mentioned above (which are all valied) outweigh the advantages of using innerHTML in practical usage? To be more concrete, let's say you are writing code to build a table dynamically (a pretty common coding task), would you use createElement or innerHTML building fragments for both approaches.
  • oninea
    oninea almost 14 years
    @Anurag: For your first example on innerHTML, I would tend to write it this way. It's a tad longer I know but personally, it's readable to me, even the structure of the fragment is maintained. Any opinion on that? array.push("<div>"); array.push("<span class='", person + personClass, "'>", personName, "</span>"); array.push("<span class='", time, "'>", date, </span>"); array.push("</div>"); someElement.innerHTML = array.join("");
  • Chance
    Chance almost 14 years
    put the code inside backticks var a = hello, but I can figure out your code. That looks more readable than a single concatenated string.
  • Matthew Crumley
    Matthew Crumley almost 14 years
    I generally avoid innerHTML because of its drawbacks. For complex markup (like building a table), I usually write functions to generate each part of the markup. For example, I would have a function that generates a tr from the data for each row. Then I might have another function that combines the rows into a table. Each function could be as simple as calling make with the appropriate arguments. If performance ever becomes a problem, I can change the functions to return HTML strings.
  • Chance
    Chance almost 14 years
    Also, dealing with tag/attribute openings and closings still remain annoying even if broken down into various steps. Instead, string replacements are much cleaner and more error-proof. I've added an example above in my answer. It might miss out on the string construction micro-optimization required for IE, but you shouldn't complicate your code for one browser unless you really need to.
  • oninea
    oninea almost 14 years
    Could you expound on the drawbacks?
  • Matthew Crumley
    Matthew Crumley almost 14 years
    @oninea: Just the ones mentioned in my answer. I could add more detail s if you need it.
  • jpillora
    jpillora over 11 years
    innerHTML faster ? Not according to jsperf.com/innerhtml-vs-createelement-and-appendchild
  • Chance
    Chance over 11 years
    I don't think that example is conclusive enough to prove anything. You need to create DOMs of varying sizes and complexity (depth) using innerHTML, and manual construction, and if the results are consistent, that would clearly show that innerHTML is slower. Also that test is highly skewed because it forces the rendering engine to invalidate the entire DOM for the body tag, and reconstruct it. While it only appends a new node to the DOM in the other example, and does not touch the other parts of the body tag.
  • Sanjeev
    Sanjeev over 9 years
    I agree with @Matthew for view on manipulating existing DOM elements, but foe creating new large DOM like table with hundreds of row we should use String concat way instead of document.createElement(), it is faster across browsers. See me detailed analysis below.
  • bobobobo
    bobobobo over 6 years
    It seems that .innerHTML() is faster because you only need 1 call to .innerHTML() for a large addition to the document. For a small amount of HTML, document.createElement() seems to be faster, but for a large amount of HTML, using the browser's capabilities via .innerHTML() seems to be faster.
  • T4NK3R
    T4NK3R over 5 years
    I think you just disproved your point. The innerHTML version is way more readable/modifiable - just insert a couble of newlines where appropriate..
  • Luís Assunção
    Luís Assunção about 4 years
    For reasons explained above by Matthew Crumley and the advantages, using document.createDocumentFragment() makes createElement infinitely better and faster then innerHTML. developer.mozilla.org/en-US/docs/Web/API/Document/…
  • Luís Assunção
    Luís Assunção almost 4 years
    createElement is light years faster than innerHTML, if you use document.createDocumentFragment().
  • sean
    sean over 3 years
    Beware of HTML injection when using this method.
  • MMMahdy-PAPION
    MMMahdy-PAPION over 2 years
    innerHTML in must of cases is much slow depend to createElement + setting options, Also will stop mistakes and handle it, like some possible character of the text inside should be escaped like &'"<> while createElement + textContent will do it automatically, but in other hand by innerHTML you have to do it manually.