Tuesday, October 16, 2007

.innerHTML and .innerText slow performance under Internet Explorer


Here's another one of those situations where you write some HTML/JavaScript using a standards-compliant web browser such as Firefox, then have to correct any issues that appear under Microsoft's Internet Explorer.

In this particular case, I was working on a JavaScript-based search form that performed special search functions on data in an IFRAME, supporting restrictions on location and the use of regular expressions. This involved traversing the HTML DOM tree, very much like the high-performance approach described in Java: Iterating over XML DOM Nodes, then using the .innerHTML and/or .textContent || .innerHTML properties to further analyze - you guessed it - the text content.

.textContent and .innerText

The .textContent / .innerHTML properties are a bit of an issue themselves. .textContent is "standards-compliant", defined by the W3C. As Microsoft admits on their MSDN page, .innerText has "no public standard that applies to this property".

To make matters worse, many developers seem to find obscure ways of detecting which property to use, often by using browser detection or other similarly flawed methods like detecting for other browser properties - e.g. using document.all - which aren't even related to the issue. It doesn't help that even the most popular JavaScript libraries don't provide a helper method for this issue. IMO, ".textContent || .innerText" is the best fix. This works due to the conditional logic in JavaScript. If .textContent returns a value that can be evaluated to true, it will be used as the result. Otherwise, .innerText will be returned. 0, -0, null, false, NaN, undefined, or the empty string ("") all evaluate to false. In Internet Explorer, .textContent returns undefined, so .innerText is used.

The Performance Issue

An almost equal performance issue with both .innerText and .innerHTML quickly became apparent under Internet Explorer. Basic testing showed that iterative use of these properties took on the order of 100x longer than the .textContent / .innerHTML properties under Firefox. (Firefox seems to work almost instantaneously, so increasing the length of the iteration quickly yields an exponential growth in the difference. Additionally, these properties seem to be internally cached by Internet Explorer, such that calling the property repeatedly on the same node results in almost immediate results after the initial call - important for testing this.)

Unfortunately, there don't appear to be any good (and free) JavaScript profilers for Internet Explorer. (Firebug and the "Venkman" JavaScript Debugger extensions exist for Firefox.)

Below is some sample JavaScript to demonstrate the issue. It will generate the specified number of <span> elements within a <div>, then time itself as it calls the selected property on each node.

On my machine, Firefox consistently completes any of the function selections in less than 500 ms (1/2 second) for 100,000 items. Internet Explorer consistently takes > 30,000 ms (30 seconds) except for the "null" selection (a dummy function that simply "returns null"), which performs as fast as Firefox.

Items: Function:


(As much as I'd like to include this as an attached page, Blogger doesn't currently support non-image file attachments.)


The only work-around I know of at the moment is to "write your own" method to retrieve the text content. .nodeValue may work for text nodes. Issues include making sure a text node is being referenced, and concatenating the values from all the children (and their children...) if needed, which matches the functionality of the above built-in properties. Fortunately, in the controlled data I'm working with, this is not the case so a simple call to .nodeValue is working... for now.


Anonymous said...

I've been having problems with this exact same thing, could you explain the workaround in a bit more detail?

FF/Safari/Google Chrome insert the HTML content almost instantly, but IE takes several seconds longer...

Mark A. Ziesemer said...

Chris - what do you need more detail on? Does .nodeValue not work for you?

My heart speaks said...

I am also pretty much facing similar issue. I have large chunk of HTML generated out of XSL transformation and when I use innerHTML property to set the generated HTML to DIV, it takes long time.
document.all[destination].innerHTML += workspaceHtml;
I am tied to IE..any IE specifc solution would work for me.

Unknown said...

.innerText is increasing the cpu usage in my application in IE.. I am using .innerText to set the text for the labels...is there another alternative to set values for labels...very urgent issue...help appreciated

Mark A. Ziesemer said...

"My heart speaks" - appending to .innerHTML, and doing so in a loop, certainly will certainly expose this performance issue. Please investigate using the DOM methods, e.g. .appendChild instead.

sheeba - Are you setting .innerText in a loop, or doing so for many labels? Regardless, try replacing with DOM calls (above in this comment), or using .nodeValue (above in the post).

Unknown said...

Hey: Quirksmode.org has a very interesting page about innerHTML vs DOM manipulation (in terms of speed).Take a look. Although the numbers are out of date, it is easy to run your own tests with each browser.

.innerHTML is frequently one of the slowest operations I find in IE7. I'll have to try .nodeValue. Also, have you tried getting .innerHTML on the parent node and parsing the HTML yourself? Depending on your use case, this could be faster, especially with a regular expression.

Anonymous said...

Good post man! Really helped me out. Here is helper function if someone is still struggling with this.


Tato szachisty said...

Because of this post I was scared so much !!!

But it presents false image how innerText is working. Because in "normal" circumstances you will never have more than 1k text nodes even in very big text document so its working like a charm everywhere.

I'm doing performance search in google before all major work (because I have to support IE8) and this example was very misleading.

I did another test with 100k chars with 1k paragraphs and to my amusement it was working with almost the same speed as getting 1k paragraphs with 10 chars per paragraph.