While looking for improvements to injecting HTML fragments into a document (which I mentioned, in passing, when I looked at using Document Fragments) I decided to spend some more time with Internet Explorer’s insertAdjacentHTML method.
This method has been in Internet Explorer since version 4.0 – as well as is in the current release of Opera – and allows you to inject fragments of well-formed HTML into a variety of locations in a document.
The locations work as such (I list the equivalent terminology):
.insertAdjacentHTML("beforeBegin", ...) |
before |
.insertAdjacentHTML("afterBegin", ...) |
prepend |
.insertAdjacentHTML("beforeEnd", ...) |
append |
.insertAdjacentHTML("afterEnd", ...) |
after |
The method is only available on DOM elements (which makes sense) and is easy to use:
var ul = document.getElementById("list"); ul.insertAdjacentHTML("beforeEnd", "<li>A new li on the list.</li>"); ul.insertAdjacentHTML("beforeEnd", "<li>Another li!</li>");
At first glance the method appeared to work well and seemed to be relatively fast. Two questions remained, though: How fast is it in comparison to using the Document Fragment technique I outlined before and does it work for all the strange use-cases that exist?
- I created a test case to compare the three types of injection: The type we’ve been using in jQuery prior to the upcoming 1.3 release, the new Document Fragment technique we’ll be using in jQuery 1.3, and a case using insertAdjacentHTML (where applicable). While both the Document Fragment and insertAdjacentHTML cases were significantly faster than the old techniques used in jQuery the Document Fragment technique ended up being marginally faster in IE 6 (50ms vs. 80ms for insertAdjacentHTML).
- There’s a huge problem with insertAdjacentHTML: It doesn’t work on all HTML elements in IE 6 (specifically it doesn’t work on table, tbody, thead, or tr elements). Having gaps in the functionality is very undesirable (attempting to use insertAdjacentHTML on those elements causes an exception to pop up in IE 6).
- It doesn’t work on XML documents. Of course neither does innerHTML (at least not until browsers start to implement HTML 5 more completely). We’re stuck doing the traditional techniques used in libraries like jQuery.
So why spend all this time talking about a method that is relatively half-baked in the main browser that implements it? Because it’s going to be part of the HTML 5 specification. This means that we’re going to see a larger number of browsers start to implement this method (and hopefully it’ll encourage existing vendors to implement it more completely and efficiently).
Having browsers implement this method will dramatically reduce the amount of code needed to write a respectable JavaScript library. I’m looking forward to the day in which this method is more-widely available (along with querySelectorAll) so that we can really buckle down and do some serious code simplification.
Jonathan Snook (November 25, 2008 at 12:01 am)
Once again, we see Microsoft continue to be an innovator, from this to innerHTML, to XMLHttpRequest, to all of the HTML5 stuff that’ll be in IE8. When will Firefox, Safari and Opera just give up and use IE as the default engine?
Mike (November 25, 2008 at 12:04 am)
Snook, nice trolling there. GTFO.
Dj Gilcrease (November 25, 2008 at 2:36 am)
With your test case I get
Chrome:
Time B: 6
Time A: 163
IE8b2:
Time B: 34
Time A: 1028
FF3:
Time B: 32
Time A: 399
aimxhaisse (November 25, 2008 at 3:35 am)
It sounds nice to have a native implementation of these overused functionalities :)
pd (November 25, 2008 at 4:11 am)
@snook
I’ll bite:
How innovative were they between August 2001 and October 2006?
5 years of security and stability updates trying to plug holes in the sieve-ware then drip-feed releasing the half-arsed IE7 is not really that innovative is it?
How’s that border-radius coming along? Be ready for version 10 maybe? Probably not since too many standards-based, easy, ‘flashy’ rounded corners might be a bit too compromising for the potential of that flashy Silverlight?
John Resig (November 25, 2008 at 8:21 am)
@Mike, pd: Haha, you guys. Jonathan got you hook-line-and-sinker. You should probably look and see who he is or what he does before replying, next time.
kirk cerny (November 25, 2008 at 10:36 am)
I really appreciate that there is someone out there willing to find this stuff out, and offer it for me to read.
Mingus Babcock (November 25, 2008 at 12:13 pm)
I found I needed to use insertAdjacentHTML (or insertAdjacentELement) in IE6&7 to insert VML elements into a document. Using those methods seemed to mitigate the strange “Error: Failed” that IE would (occasionally) throw once I tried to manipulate the new elements in place.
steve (November 25, 2008 at 1:52 pm)
Nice post John! Since IE already has confirmed bugs with .innerHTML on Tables in IE6, IE7, IE8 (Beta2) & has confirmed they have no intention of fixing them for IE8 RTM.
https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=336254
I suspect that a working version of .insertAdjacentHTML will likely not be addressed until at least IE9.
Also, setting .innerHTML on a Select element in IE causes bugs so I’m going to “expect” that if you try to insert options before or after the current option it too will “likely” suffer from bugs.
Scott (November 25, 2008 at 1:58 pm)
It is very easy to write your own substitute for this in the meantime:
function(htmlStr) {
var tmpElem = document.createElement('div');
tmpElem.innerHTML = htmlStr;
return tmpElem.firstChild;
}
htmlStr is parsed and turned into DOM elements when you add it to the temporary element. Of course this function will only return the first node in the html that is passed in, and not all elements are legal inside a DIV, but you can write your own implementation that suits your needs.
John Resig (November 25, 2008 at 2:29 pm)
@Scott: Unfortunately it’s not really easy to write one of these – a large chunk of code is dedicated to implementing just this in jQuery. This is why I was looking to use insertAdjacentHTML as a fallback solution but it doesn’t really fit that need any better than what we have right now.
Michael Jackson (November 25, 2008 at 5:42 pm)
@Snook: Amazing. Even knowing full well who you are, and what you do, somehow your comment still got under my skin. All those countless hours of time spent working out IE quirks is forever permanently encoded in my DNA or something…*quiver*…nice one. ;)
@John: In regards to your second bullet point (about insertAdjacentHTML throwing exceptions in IE6 when used on <table>s) there is a hack to make it work. There is some nasty code in Ext’s DomHelper.js that essentially creates temporary table fragments on the fly (by appending innerHTML to a <div>) and then uses insertBefore while referencing the fragments. Like I said, it’s naaaasty (it even says so in the source), but it gets around the issue you stated.
I’m not suggesting you use it, just more info for the discussion I guess.
Are there any other elements that you’re aware of that IE6 complains about when using insertAdjacentHTML? Jack and company seem to believe that <table> and friends are the only ones.
kik (November 26, 2008 at 9:08 am)
“It doesn’t work on all HTML elements in IE 6”
No, really? :]
Concerning the first bullet : and what about the compared speed of the new Document Fragment method and insertAdjacentHTML in other versions of IE and in Opera? If insertAdjacentHTML is faster, would it be more time consuming to decide to use insertAjacentHTML or the document fragment method, depending on the used browser?
John A. Bilicki III (January 24, 2009 at 5:19 am)
You may want to have a gander at this…
http://www.jabcreations.com/web/javascript/elements.php
Adding an element before, after, or inside another element these functions I put together work in IE 5.5+, Opera 7, Gecko 1.6+ with class support (Gecko 1.0+ without), and don’t have an overpriced PC to test older versions of WebKit out though Safari 3.0 works fine too.
The issue with innerHTML (and I presume insertAdjacentHTML) is that the DOM won’t see the elements created where as with the functions on the page I’ve linked will.