Paul Irish

Making the www great

Surefire DOM Element Insertion

So you’ve got an element. You need to add it to the DOM. Somewhere, anywhere, just be sure that it works.

In Modernizr, we need to do this all the time. In order to adequately do our feature detection we need to try out some elements in the DOM. But how exactly to add it? Steve Souders dug into appendChild vs insertBefore and the comments meandered through the sea of possibilities. Here’s the best way to add yourElem:

1
2
var ref = document.getElementsByTagName('script')[0];
ref.parentNode.insertBefore(yourElem, ref);

But you prefer a good old document.body.appendChild(elem); Awww! I know, man. I used to, too. Well, you can’t always trust this.. Especially if you’re writing library, widget or third-party code.

Here’s why you can trust this insertBefore method, and why all the other techniques are not wise:

  • appendChild can throw Operation Aborted
  • <head> doesn’t always exist in old opera, not-so-old safari, and others
  • if your script runs in the <head>, the <body> doesnt exist yet.
  • appending to document.documentElement can lead to an uncertain parentNode.
  • document.documentElement.firstChild (or .childNodes[0]) may be an html comment.
    • and as of FF4, insertBefore doesnt work with an html comment node

Okay okay okay, there is actually one case where the above method poses a problem:

  1. You are inserting a <link>, <script>, or <img> (sumpthin’ referencing another asset).
  2. Said asset has a relative URL. (Yours has an absolute URL? You’re A-OK, bud)
  3. The page has a <base> element, changing the base URL used to resolve all relative URLs used.
  4. The first <script> element of the page is after said <base> element.
  5. You don’t want your element’s asset path to be affected by the <base> element.

Wow. So if this scenario is a concern for you.. Then you need a small addition to the earlier code:

1
2
var ref = document.getElementsByTagName('base')[0] || document.getElementsByTagName('script')[0];
ref.parentNode.insertBefore(yourElem, ref);

But anyway that’s a pretty narrow edge case so most people will be fine with the earlier snippet.

And if you have a hard time remembering the signature for insertBefore like I do, just think that your parent wants you to insert yourself before your younger brother. That’s totally parentNode.insertBefore(you, sibling), right? :)

Thx souders, alex sexton, miketaylr, ben alman, jdalton & everyone else who cares about these small details

Comments