Blog


Bad Object Detection

A common technique for writing cross-browser JavaScript code is to detect the features that you wish to use before you actually use them. Good object detection is done on a case-by-case basis, analyzing each feature as it's encountered.

Some common examples of object detection:

// Is XPath querying available?
if ( document.evaluate ) {}
// Does the element have style information?
if ( element.style ) {}
// Can you get the computed style?
if ( document.defaultView && document.defaultView.getComputedStyle ) {}

All that these are doing is detecting if these particular properties are available before trying to use them. If you were inclined you could take these a step further and attempt to verify the types of the object properties as well (such as verifying that document.evaluate is a function).

Now one common problem - and one that's as old as JavaScript itself - is to use object detection as a means of making sweeping generalizations concerning a set of features or an entire browser.

The classic bad one is:

// Are we in IE?
if ( document.all ) {}

The obvious problem, with the above, is that it is capable of returning 'true' in browsers that aren't Internet Explorer (such as Opera). The difficulty arises when the developer assumes that an Internet Explorer-exclusive feature is available based upon the existence of that simple - and completely unrelated - property.

That takes into account the obvious 'enemy' - an unknown browser - however there's a far bigger threat: Unintended consequences introduced by libraries or obscure fringe cases introduced by the user or markup. Let's look at both.

Feature Introduction by Libraries

Libraries frequently introduce features that are missing from a particular browser. A common one, for example, is introducing document.getElementsByClassName for browsers that don't have it. At the same time I've seen code like the following:

// Are we using Firefox 3?
if ( document.getElementsByClassName ) {}

Which is incredibly problematic. To start with: That feature may be introduced, right now, by a library or other snippet causing unintended consequences in your application. Additionally future browsers will inevitably introduce this feature (such as Safari 3.1) causing this detection to go completely out the window.

Fringe Cases From Global Variables

While they don't happen often for those that they do happen to they can be a living hell. Specifically when object detection takes place within the global object (e.g. window.opera) then all bets are off as to its cross-browser applicability. Let's take a look at an example:

<html>
<head>
<script>
window.onload = function(){
  if ( window.opera ) {
    alert("You're using Opera, pick me!");
  }
};
</script>
</head>
<body> 
  <h1>Which browser?</h1>
  <a href="http://mozilla.com/" id="firefox"> Firefox</a><br/>
  <a href="http://apple.com/" id="safari"> Safari</a><br/>
  <a href="http://opera.com/" id="opera"> Opera</a><br/>
  <a href="http://microsoft.com/" id="ie"> Internet Explorer</a>
</body>
</html>

You would expect the alert to pop up when the user visits the page in Opera - and it does - however the alert also comes up in Internet Explorer. This is because all elements with an ID are automatically introduced as global variables (in the above there are four globals: firefox, safari, opera, and ie that all reference their associated elements). This is a fantastically difficult bug to spot and work around - thus you should be very cautious when doing any form of object detection on the global object.

Of course, it almost goes without saying, that there could exist an 'opera' variable elsewhere in the site that could interfere with this detection. Although that would, arguably, be easier to detect as it would make every browser trigger a false positive.

There's two morals here:

  1. Only use object detection to detect exactly what you need to use.
  2. Be wary of detecting properties on the global object - it's capable of lying to you.

Tags: javascript, programming, browsers

JavaScript Performance Stack

Something that's frequently befuddled is the differentiation between where JavaScript is executing and where performance hits are taking place. The difficulty is related to the fact that many aspects of a browser engine are reliant upon many others causing their performance issues to be constantly intertwined. To attempt to explain this particular inter-relationship I've created a simplified diagram:

To break it down, there's a couple key areas:

  • JavaScript - This represents the core JavaScript engine. This contains only the most basic primitives (functions, objects, array, regular expression, etc.) for performing operations. Unto itself it isn't terribly useful. Speed improvements here have the ability to affect all the various object models.
  • Object Models - Collectively these are the objects introduced into the JavaScript runtime which give the user something to work with. These objects are generally implemented in C++ and are imported into the JavaScript environment (for example XPCOM is frequently used by Mozilla to achieve this). There are numerous security checks in place to prevent malicious script from accessing these objects in unintended ways (which produces an unfortunate performance hit). Speed improvements generally come in the way of improving the connecting layer or from removing the connecting layer altogether.
    • XMLHttpRequest and Timers - These are implemented in C++ and introduced into the JavaScript engine at runtime. The performance of these elements only indirectly affect rendering performance.
    • Browser - This represents objects like 'window', 'window.location', and the like. Improvements here also indirectly affect rendering performance.
    • DOM and CSS - These are the object representations of the site's HTML and CSS. When creating a web application everything will have to pass through these representations. Improving the performance of the DOM will affect how quickly rendering changes can propagate.
  • Parsing - This is the process of reading, analyzing, and converting HTML, CSS, XML, etc. into their native object models. Improvements in speed can affect the load time of a page (with the initial creation of the page's contents).
  • Rendering - The final painting of the page (or any subsequent updates). This is the final bottleneck for the performance of interactive applications.

What's interesting about all of this is that a lot of attention is being paid to the performance of a single layer within the browser: JavaScript. The reality is that the situation is much more complicated. For starters, improving the performance of JavaScript has the potential to drastically improve the overall performance of a site. However there still remain bottlenecks at the DOM, CSS, and rendering layers. Having a slow DOM representation will do little to show off the improved JavaScript performance. For this reason when optimization is done it's frequently handled throughout the entire browser stack.

Now what's also interesting is that the analysis of JavaScript performance can, also, be affected by any of these layers. Here are some interesting issues that arise:

  • JavaScript performance outside of a browser (in a shell) is drastically faster than inside of it. The overhead of the object models and their associated security checks is enough to make a noticeable difference.
  • An improperly coded JavaScript performance test could be affected by a change to the rendering engine. If the test were analyzing the total runtime of a script a degree of accidental rendering overhead could be introduced as well. Care needs to be taken to factor this out.

So while the improvement of JavaScript performance is certainly a critical step for browser vendors to take (as much of the rest of the browser depends upon it) it is only the beginning. Improving the speed of the full browser stack is inevitable.

Tags: javascript, browsers, performance

JavaScript and Browsers at SXSW

It's that time of year again - the annual South by Southwest conference is nearly upon us! This year I submitted two panel topics and had one accepted: Secrets of JavaScript Libraries.

Secrets of JavaScript Libraries

This is going to be a stand-out panel, I can feel it in my bones:

This talk will delve into the secret techniques used by JavaScript library authors to create comprehensive libraries that work seamlessly across browser environments. We'll look at everything from fixes for strange browser quirks, tricks for gaining speed, to tips for writing smooth animations. This panel will be held by experienced JavaScript Library developers who have, cumulatively, many decades of JavaScript development experience under their belts. Everything discussed will be backed up with publicly available, rock-solid, code.

And check out who will be joining me on the panel:

  • Sam Stephenson (Creator of Prototype)
  • Alex Russell (Co-Creator of Dojo)
  • Thomas Fuchs (Creator of Scriptaculous)
  • Andrew Dupont (Contributor to Prototype)

This is going to be an advanced panel, tons of code, deep-and-dirty into the code and why we do what we do. So be sure to check us out - we're going to be competing against the keynote talk on the final day: Room 9 - Tuesday, March 11th 2 to 3:00pm.

Browser Wars: Deja Vu All Over Again?

One of the top panels from last year, back again. Three of the major browser vendors will be up and discussing various aspects of browser development, standards, and all sorts of interesting topics. Be sure to stop by to get your dose browser craziness in: Room 10 Monday, March 10th, 2 to 3:00pm.

On the panel:

  • Chris Wilson (Platform Architect, Microsoft)
  • Brendan Eich (CTO, Mozilla)
  • Charles McCathieNevile (Chief of Standards, Opera)

Birds of a Mozilla Feather

There's also going to be a Birds of a Feather, held by Mozilla, on Monday, March 10th from 3:30 to 5:30pm (soon after the Browser Wars panel) at the Moonshine Bar and Grill. This is going to be a ton of fun, it'll be a good time for chatting and talking about the future of Firefox and the web. You can expect to see the following people there:

  • Brendan Eich (Creator of JavaScript)
  • Christopher Beard (Head of Mozilla Labs)
  • Aza Raskin (Mozilla Labs)

If you wanna catch up with me, be sure to do it before Tuesday as my flight is right after the panel. I'll be spending plenty of time checking out the various parties, panels, etc. so you're bound to see me around.

If you're not already doing so you should follow me on Twitter as that'll probably be the best source of determining where I am (and for pinging me if you want to meet up).

I'm so looking forward to being in Austin. Boston is expecting 3-5in of snow tonight. Sunny, warm, Texas can't come soon enough.

Tags: javascript, browsers, sxsw, conferences

Revenge of the Timers

After my earlier post on analyzing JavaScript timer performance a lot has changed, especially in Firefox 3. An excellent patch landed the other day which is giving us some excellent performance improvements - not the least of which is related to timers.

If you remember from before I took a look at how responsive JavaScript timers were on OSX in Firefox 2, Safari 3, Firefox 3, and Opera 9. The conclusion, at least for me, was that Firefox 3's timers were much improved over Firefox 2's, but could stand some improvement.

I filed a couple bugs, relating to the matter:

And to my delight this new patch completely resolves all of them. Let's take a quick peak at the results. These are the results of doing setInterval(fn, 0); in Firefox 2, Safari 3, Firefox 3, and Opera 9. Firefox 3 is in the bottom-left corner, note the improvement.

Before

After

The change in quality is startling. It's easy to say that Firefox 3 and Safari 3 are quite comparable now, in terms of overall timer quality and, if nothing else, a major improvement over the timers in Firefox 2.

If you want to try this improvement for yourself feel free to download a nightly of Firefox 3 and head on over to the timer test page, I think you'll be quite pleased.

Tags: firefox, javascript, browsers

Most Bizarre IE Quirk

I was playing around with timers the other day, trying to see how different browsers handled different edge cases, when I stumbled across a fascinatingly bizarre quirk in Internet Explorer. I wouldn't call it a bug, per se, since that would imply that there was an excepted result, but there is not. The code:

setInterval(function(){
    alert("hello!");
}, -1);

Now, before you view the demo (view in IE only!) try to guess what the above code does.

Since -1 isn't a valid interval my guess was that it would, either (based upon Internet Explorer's current setInterval behavior):

  1. Execute immediately, like a normal function, but then never fire again.
  2. or not execute at all.

What happened is positively bizarre: The callback function will be executed every time the user left clicks the mouse, anywhere in the document.

I find this to be absolutely hilarious. I'm struggling to think of how this could, possibly, be used in any remotely feasible way. Some random ideas:

  • A global click handler that can't be removed and doesn't leak memory (presumably since no DOM elements involved?)
  • Some sort of vector for XSS attacks. I don't know what the situation would be where you have access to setInterval but not the DOM, so I'm not sure what credence this has.
  • A way to initiate a fake event capturing click event (before the actual bubbling event occurs).

I don't know - I'm not sure if any of them are terribly useful but I find it to be amusing nonetheless. Thoughts on how we can have fun with this?

Tags: bugs, javascript, browsers

XPath Overnight

A fascinating thing has happened in the world of JavaScript DOM traversal: Over the course of a couple months in 2007 three of the major JavaScript libraries (Prototype, Dojo, and Mootools) all switched their CSS selector engines to using the browser's native XPath functionality, rather than doing traditional DOM traversal. What's interesting about this is that the burden of functionality and performance has, literally, flipped overnight on to the browser's XPath engine, from its pure DOM implementation.

There's some really interesting things about this switch:

  • Native XPath is blazing fast. For a majority of CSS selectors it completely trumps using native DOM methods (like getElementsByTagName, for example). Sometimes it pays to special-case your code for selectors like #id, but overwhelmingly XPath is the direction in which JavaScript libraries are heading.
  • Since a large percentage of JavaScript users use JavaScript libraries (and, thus, use the behind-the-scenes XPath, as well) this means that browsers are now spending significantly more time processing XPath queries than they ever were before. This means that the performance field is now, effectively, split between two areas: Traditional DOM querying and XPath.
  • No one is analyzing the performance of browser XPath queries. Or, if they are, it's certainly not public. I'm working on some new XPath performance tests, in order to bring them some more visibility, and hope to have them released this week.
  • XPath, while incredibly useful, is a black box. The developer has no control over how fast the results come back - or if they are even correct. Contrast this with traditional DOM scripting (where you can fine-tune your queries to perfection). Browsers will always be bound to have some bugs in their implementations. For example, Safari 3 isn't capable of doing "-of-type" or ":empty" style CSS selectors, nor is any browser able to access the 'checked' property, or namespaced attributes (all noted in Prototype's implementation) which means that they have to fall back to a traditional DOM scripting model.
  • Internet Explorer is a dead-end. Since most users want a CSS selector implementation that will work against HTML documents - and IE is unable to provide one - all CSS Selector implementations must provide two (2) side-by-side selector engines in order to handle these cases (not to mention the aforementioned cases where browsers provide unexpected behavior).

A couple things to take away from all of this:

  1. XPath (and new methods like querySelector) are the way of the future for a lot of JavaScript libraries - and the next frontier for browser optimization.
  2. These implementations are black boxes that are unable to be modified by the developer (leaving them vulnerable to browser bugs).
  3. A dual DOM-only CSS selector engine must be provided well into the foreseeable future, by libraries, in order to account for browser mis-implementations.

I should, also, probably answer the inevitable question: "Why doesn't jQuery have an XPath CSS Selector implementation?" For now, my answer is: I don't want two selector implementations - it makes the code base significantly harder to maintain, increases the number of possible cross-browser bugs, and drastically increases the filesize of the resulting download. That being said, I'm strongly evaluating XPath for some troublesome selectors that could, potentially, provide some big performance wins to the end user. In the meantime, we've focused on optimizing the actual selectors that most people use (which are poorly represented in speed tests like SlickSpeed) but we hope to rectify in the future.

Tags: performance, libraries, javascript, browsers, dom

The Performance Paradox

Just because a test is good at measuring performance for one metric, doesn't mean that it's good for all metrics. The other day I posted about some JavaScript Library Loading Speed Tests that were done by the PBWiki team. I made some conclusions about JavaScript Library Loading speed that, I think, were pretty interesting - however, I mentioned some browser load performance results (at the end of the post) which were especially problematic. This brings up an important point from the performance results:

User-generated performance results are a dual-edged sword.

Assuming that there's no cheating involved (which is a big assumption) then quietly collecting data from users can provide interesting results. HOWEVER - how that data is analyzed can wildly effect the quality of your results. Analyzed correctly and you can start to get a picture for how JavaScript libraries perform on page load, incorrectly and you might assume that specific browsers are broken, slow, or providing incorrect results.

There's a ton of examples of misinformation, relating to browsers, within the "Browser Comparison" results. I'm just going to list a bunch of issues - showing how much of a problem user-generated browser performance data can be.

  • The results show the numbers for Opera being heavily skewed. At first glance one might assume "oh, that's because Opera is slower at loading JavaScript files" - however this is not the case at all. Instead, a more-plausible answer is that users were testing this site from a copy of Opera Mobile (which performs poorly, compared to a desktop browser).
  • Both Safari 2 and Safari 3 are grouped together, which is highly suspect. By a number of measurements Safari 3 is much faster than Safari 2, so having these two merged doesn't do any favors.
  • Firefox 3 only has two results. A commenter mentioned that this was because they were being grouped into the "Netscape 6" category - which, in and of itself, is a poor place for conglomeration.
  • IE 7 is shown as being faster as IE 6. This may be the case, however it's far more likely that users who are running IE 7 are on newer hardware (think: A new computer with Vista installed), meaning that, on average, IE 7 will run faster than IE 6.
  • Firefox, Opera, and Safari for Windows users are, generally, early adopters and technically savvy - meaning that they're, also, more likely to have high performance hardware (giving them an unnecessary advantage in their results).
  • No attempt at platform comparison is given (for example, Safari Window vs. Firefox Window and Safari Mac vs. Firefox Mac). Having the results lump together provides an inaccurate view of actual browser performance.

There's one message that should be taken away from this particular case: Don't trust random-user-generated browser performance data. Until you neutralize for outstanding factors like platform, system load, and even hardware it becomes incredibly hard to get meaningful data that is relevant to most users - or even remotely useful to browser vendors.

Tags: performance, browsers

The Browsers of 2009

In a follow-up to this question I've begun pondering what the most-relevant browsers of 2009 will be. I tend to determine relevance by the question "Is this browser cost beneficial to us supporting it" being answered by a significant number of developers and corporations.

For example, I would probably rate the current, 2008, list as follows (in order of cost-benefit):

  • IE 6
  • IE 7
  • Firefox 2
  • Safari 2 & Safari 3 (tie)
  • -- common cut-off point --
  • IE 5.5
  • Opera 9.2

Starting in 2009 I predict that the list will be similar to the following:

  • IE 7
  • IE 6
  • Firefox 3
  • Safari 3
  • -- common cut-off point --
  • Opera 9.5

Not a whole lot changed, but a couple points to consider:

  • I wouldn't be surprised if the switch between the dominance of IE 6 and IE 7 finally occurred.
  • Firefox 3 will probably be released late spring/early summer - thankfully uptake will be fast (as it has, generally, been with past versions).
  • Safari 3 is already shooting way up, Safari 2 will be a thing of the past come 2009.
  • Opera will definitely have 9.5 out by then - maybe 10? Although, even if 10 is out, it definitely won't have a market share by next January.
  • IE 5.5 will definitely squeeze out it's last remaining % into cost-benefit obscurity.

What's interesting about analyzing the cost-benefit of a browser is that it's done completely differently from straight-up analysis of browser market share. It's really a combination of market share and time that'll be spent customizing your application to work in that browser. Here's a quick chart to represent my choices:

The "Cost" is represented by the % of time that will be spent, beyond normal application development, spent exclusively with that browser. The "Benefit" is the % of market share that the browser has. Note that any browser that has a higher cost, than benefit, needs to be seriously considered for development.

What's interesting about this is that it use to be much-more clear-cut when choosing to develop for IE 6 - it had 80-90% market share so it's benefit was always considerably higher (or, at least, equal to) the time spent making it work in that browser. However, in 2009, that percentage will be considerably less (I'm estimating 35%, to IE 7's 45%) making it far less attractive as a platform. Note that Firefox and Safari, due to their less-buggy nature (and standards compliance) always have a higher benefit than cost, making them an easy target to work towards. Opera is problematic, however. It's, continued, low market share makes it a challenging platform to justify. It's for this reason that major frameworks, like Prototype, have ignored working with Opera up until this point - and understandably so.

Now it's not always a one-to-one trade-off in-between cost and benefit. I think it would even be safe to say that benefit is, at least, twice as important as cost. Ultimately, however, this depends upon the choices of those involved in the decision making and the skill of the developers working on the compliance. Is an extra 4-5% market share from Safari 3 worth 4-5 developer days? What about the added overhead to Quality Assurance and testing? It's never an easy problem - but it's one that we can, certainly, all get better at over time - but really only through hard work and experience.

Tags: browsers

Next entries » · « Previous entries

Current Projects

jQuery JavaScript Library

jQuery

Comprehensive DOM, Event, Animation, and Ajax JavaScript Library.

Recent Projects

Pro JavaScript Techniques

JavaScript Book

The best techniques for professional JavaScript. Published by Apress.