Category Archives: Javascript

Using console.time to test strategies appending HTML while in loops: createElement, innerHTML, and jQuery append.

I have long heard that innerHTML is faster than creating elements, when you don’t need to store the DOM element in Javascript for further use. I had to do one such manipulation on Recognize’s pricing page.

Recognize’s pricing page copies text from the right features column for mobile considerations.

On page load our pricing page grabs the text of the features on the right column, and inserts the same text into the corresponding row. You can’t see it unless you resize your browser down to the size of a phone. We show the hidden copied text for mobile devices, because we can’t fit that right features column on 480px width devices, like the iPhone.

Recognize pricing page on an iPhone 4

Recognize pricing page on an iPhone 4

I want this to happen fast. Because I loop over the DOM, the data is stored in the existing HTML. This is expensive, but allows me to keep my data DRY, and allow for devices that don’t have JS to at least view the site. Not to mention that it is important for the features to be in the HTML for SEO.

I profile my functions with console.time and console.timeEnd. For instance:

  function funStuff() {
    console.time("funStuff finished");
    // Do something expensive
    console.timeEnd("funStuff finished");
  }

That will output a time in the console. You can learn more about console helpers at Firebug’s Console API docs.

Let’s get down to business with my code on the Pricing page. Here it is with my first attempt, using document.createElement to append the HTML:

  P.prototype.copyFeatureText = function() {
    var $features = $("#features li");

    $(".features").each(function(j, el) {
      var pricingFeature = this;
      $features.each(function(i, el) {
        var span = document.createElement("span"),
            listItem = pricingFeature.getElementsByTagName("li")[i],
            text = this.innerText;

        span.innerText = text;
        listItem.appendChild(span);
      });
    });
  };

I’m doing a lot here. I’m looping over each feature’s list (Startup Package and Business Package), and for each line I create a new span and give it the same text as the corresponding feature’s list. It looks slow.

I run this 10 times to have a decent sample size.

The results in milliseconds:
43.698
44.765
45.872
45.381
45.101
45.932
57.034
37.946
36.279
44.404

Using document.createElement, this operation took on average 44.641ms.

Okay, I rewrote copyFeatureText function to not store so many variables and to use innerHTML instead of createElement.

  P.prototype.copyFeatureText = function() {
    var $features = $("#features li");

    $(".features").each(function(j, el) {
      var pricingFeature = this;
      $features.each(function(i, el) {
        pricingFeature.getElementsByTagName("li")[i].innerHTML += ""+this.innerText+"";
      });
    });
  };

I run the profiling again. Here are the results in milliseconds:

56.768
51.154
56.942
45.673
49.746
38.897
45.057
48.204
53.077
51.538

With string concatenation and innerHTML, it takes slightly longer at 49.706ms.

The results show that for this specific operation, it takes around 5 milliseconds longer with innerHTML. Somewhat counter intuitive on first impression due to the decreased lines of code. But knowing more about what is happening under the hood with +=, we find that += can be a slow.

Clearly, appending elements on each list item slows down the operation. It seems for my uses it is necessary, unless I was to strip out the entire DOM list item nodes and replaced them with a documentFragment. A strategy I believe to be overkill.

To avoid += string concatenation, many suggest using Array.join(). Modern browsers, like Chrome, don’t show any difference in performance. But let’s test anyway using this strategy, plus using jQuery to append a string.

  P.prototype.copyFeatureText = function() {
    var $features = $("#features li");
    
    $(".features").each(function(j, el) {
      var pricingFeature = this;
      $features.each(function(i, el) {
        var listItem = pricingFeature.getElementsByTagName("li")[i],
            span = [];
            
        span[0] = "";
        span[1] = this.innerText;
        span[2] = "";
            
        $(listItem).append(span.join(""));
      });
    });
  };

Here’s the results:
47.092
49.399
41.382
43.525
63.440
53.579
62.801
58.524
56.857
60.428

Appending a string via jQuery takes on average 53.703ms.

That is slower. Makes sense considering it uses jQuery. jQuery does quite a lot and can slow things down a bit at times.

I can see one way I can quickly make this last example more performant, only reset the second index of the span array.

  P.prototype.copyFeatureText = function() {
    var $features = $("#features li");
    var span = ["", null, ""];
    
    $(".features").each(function(j, el) {
      var pricingFeature = this;
      $features.each(function(i, el) {
        var listItem = pricingFeature.getElementsByTagName("li")[i];

        span[1] = this.innerText;
            
        $(listItem).append(span.join(""));
      });
    });
  };

The results are better:
53.848
36.993
57.184
40.089
54.411
52.891
59.836
48.242
49.737
49.209

Only creating an array once, and only changing the text array index in the loop takes 50.244.

Okay so slightly faster.

Looking at the results, it looks like for now I’ll keep doing exactly what I was doing with document.createElement even if the difference is only a few milliseconds in Chrome.