Surefire DOM Element insertion

February 16th, 2011

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:

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:

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

11 More Things I Learned from the jQuery Source

January 19th, 2011

I recently dove back into the jQuery source code to pull out some more JavaScript goodness. If you haven't seen the original 10 Things I Learned from the jQuery Source video, definitely check that out.

But now.. some newness:


youtube link for flash-less HTML5 player

In this video:

Tune in for 29 minutes and soak it up.

Chrome's inset box-shadow bug: Fixed!

January 6th, 2011

If you've ever had inset box-shadow styles, perhaps for a button, looking like this:

.omg {
  -webkit-box-shadow: inset 5px 5px 5px red;
  border-radius: 40px;
}

You might have noticed it comes out looking pretty bad in Windows and Linux in Chrome. Why not on Mac? The Mac port uses CoreGraphics, while the others use Skia for system graphics which is where the root issue resided.

I'm happy to report the latest dev channel shipping now (10.0.628.0) has fixed this bug! Woohoo! Over 338 people starred the issue at http://crbug.com/29427. But lucky for us, Hajime Morita on the Tokyo Chrome team dug deep into the Skia code and secured a fix.

Demo link at plexode

And I should probably take this moment to remind you…

Reporting bugs is a web developer's responsibility

If you're doing heavy HTML5/CSS3/WebGL/whatever experimentation and you run into bugs in the dev channel Chrome, please report them at http://new.crbug.com.

Provide a reduced test case and your ticket will quickly be triaged and attended to. And of course, search to see if it's already been reported. ☆ any issue you'd like to vote for and monitor progress on, though don't add any "me too" or +1 comments… or I'll come after you! :)

A call for development help and research: Lazyweb Requests

December 20th, 2010

If you'd like to contribute a bit of your time back to the benefit the larger web community, I've collected a few mini-projects that'll have high impact. They're together in an issue tracker of an otherwise empty github repo: github.com/paulirish/lazyweb-requests.

Some of the things in there:

If you have some extra time this holiday break or whenever, want to learn something new and help the larger community… please dig in!

The protocol-relative URL

October 27th, 2010

There's this little trick you can get away with that'll save you some headaches:

<img src="//domain.com/img/logo.png">

If the browser is viewing that current page in through HTTPS, then it'll request that asset with the HTTPS protocol, otherwise it'll typically* request it with HTTP. This prevents that awful "This Page Contains Both Secure and Non-Secure Items" error message in IE, keeping all your asset requests within the same protocol.

*Of course, if you're viewing the file locally, it'll try to request the file with the file:// protocol.

We use this trick in the HTML5 Boilerplate for a clever request of jQuery off the Google CDN:

  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
  <script>!window.jQuery && document.write(unescape('%3Cscript src="js/libs/jquery-1.4.2.js"%3E%3C/script%3E'))</script>

Technically, this is called a "network-path reference" according to RFC 3986. Oh and if you want to be truly correct, you'll use the term "scheme" instead of "protocol" when talking about URLs.

This trick also works fine in CSS:

.omgomg { background: url(//websbestgifs.net/kittyonadolphin.gif); }

… assuming the site you're pointing to has this asset available on both HTTP and HTTPS.

Caveat: When used on a <link> or @import for a stylesheet, IE7 and IE8 download the file twice. All other uses, however, are just fine.

Thx to miketaylr, ralphholzmann, annevk for smarts on this, and ajaxian, where I think I learned it like 4 years ago? maybe?

2011.01.23: But.. what about using this on the google analytics snippet?

Yes of course, wouldn't that be nice.. So I worked with the Google Analytics javascript lead developer (God, I love working at google) to see if we could do this.. turns out we can't. There is an edgecase bug in IE6 that causes a dialog to blow up… under some security settings (unsure if they are default) when requesting form the non-'ssl' subdomain. screenshot here. So feel free to take 40 bytes off your GA snippet if you don't care about IE6.. otherwise you're gonna need that ternary operator. :)

2011.12.24. Eric Law (from the IE team) chimes on why IE6 doesnt play well GA…

The reason this doesn't work in IE6 is that the server is using SNI to deduce what certificate to return. XP (and thus IE6) doesn't support SNI in the HTTPS stack. See for details.