2009
07.31

Note: Whether you agree or not to this article please retweet and spread the word so that we can promote a healthy discussion here.

Here is what I think happened in the last 10 plus years or so in the cross browser scripting world; pure incompetence -> global object detection (i.e. document.all) -> user agent sniffing -> feature detection (i.e. document.querySelectorAll) -> feature simulation -> browser detect + feature detection / feature simulation

Here’s some take on object detection vs user agent sniffing which I am not going to discussing in this article.

Browser detect, whether using user agent sniffing or global object detection, is notorious for being unreliable because are highly unpredictable or can be hijacked.

Feature detect lets you know whether the browser has the method but does not tell you if it works correctly.

Feature simulation, will not only detect if the browser supports a certain method but also tells you if it works correctly or not.

It is clear that, feature simulation is the only way to go in future. End of story. Not!

So, where does browser detect come into play?

In modern JavaScript, browser detect has lost its place in cross-browser scripting and there’s no doubt about that. BUT, browsers are built differently, from different people with different personalities and philosophies and with different tools. Although browsers try their best to adhere to all the standards, the standards does not specify how implement them i.e. which algorithm to use, how to allocate/deallocate etc.

There exists different codes that will achieve the same results. Let’s look at two pieces of code that delete all descendant nodes from an element.

<div id="test"><strong>A</strong><strong>A</strong><strong>A</strong></div>
// method one
var node = document.geElementById('test');
node.innerHTML = '';
 
// method two
var node = document.geElementById('test');
var fragment = document.createDocumentFragment();
var newEl = node.cloneNode(false);
fragment.appendChild(newEl);
node.parentNode.replaceChild(newEl, node);

The benchmark results are here. Both of the code snippets are cross-browser compatible. The Gecko engine runs faster using method two, other browser runs faster using method one. Here’s how I would use browser detect to do performance optimization.

var node = document.geElementById('test'), fragment, newEl;
 
if (isGecko) {
  fragment = document.createDocumentFragment();
  newEl = node.cloneNode(false);
  fragment.appendChild(newEl);
  node.parentNode.replaceChild(newEl, node);
}
else {
  node.innerHTML = '';
}

It does not matter whether isGecko returns a false positive or false negative, the code just works. Below is a generalization on how to use browser detect to optimize your code.

if (isSomeBrowser && isSomeBrowserFeatureSupported) {
 
}
else if (isSomeCrossBrowserFunctionSupported) {
 
}

To illustrate the performance improvements, here’s a benchmark using Karmagination vs other JS libraries.

2 comments so far

Add Your Comment
  1. Definitely a great approach, particularly if the sniffing is only performance-related.

    For the node deletion code example: remember that JavaScript doesn’t have block scope. So declaring and defining the variables within their respective blocks is no different from declaring them all first and then defining them inside the blocks.

  2. @Kit You are right. Updated my code example.