dean.edwards.name/weblog/2005/12/js-tip1/

JavaScript Tip #1: Speed Up Object Detection

Welcome to the first of what I hope will be a regular series of JavaScript tips.:-)

Object detection is common in JavaScript. Browser irregularities mean that your code must sometimes contain branches for different browsers. Here is a simple way to speed up object detection for a leaner and meaner script.

Instead of this:

function addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else if (element.attachEvent) {
		element.attachEvent("on" + type, handler);
	}
};

Do this:

var addEvent;
if (document.addEventListener) {
	addEvent = function(element, type, handler) {
		element.addEventListener(type, handler, false);
	};
} else if (document.attachEvent) {
	addEvent = function(element, type, handler) {
		element.attachEvent("on" + type, handler);
	};
} else {
	addEvent = new Function; // not supported
}

This means you are only performing object detection once instead of every time you call addEvent. Obviously, this technique can be applied to any function where you are performing object detection.

I am only using a simple addEvent as it is the best example I could think of. Better techniques exist for cross-browser event handling.

Comments (35)

Leave a comment

Thanks for that little tip.

It’s simple methods like these that are often overlooked when coding and could easily be recognised if everyone took the time to step back and have a good think every now and again.

  • Comment by: Si
  • Posted:

That’s a cool approach that I haven’t thought of before… nor have I seen others use it either. But it’s a simple and effective way to increase performance.

I’m looking forward to the future tips from the JavaScript Grand Master.;-)

That is a great tip. Not really new, but great, as the most javascript developers don’t use it this way. Especially for functions which you use multiple times in a script this is a good way to optimize the performance. By the way (to Richard): Our qooxdoo toolkit use this style all over.

[…] II Wartime Images Speed Up Object Detection in JavaScript JavaScript Tip #1: Speed Up Object Detection Object detection is common in JavaScript. Browser […]

Why this is javascript-related? This kind of optimization common to many other dynamic languages.

  • Comment by: YuppY
  • Posted:

Damn. We overlook the simple stuff. Thanks Dean. And that was so short and simple. I’ll definitely have to reconsider the other places I do object detection. In general, for window ‘load’ events, I run object detection at the very beginning and run a short return (semi-bad practice) if the objects are not supported.

Anyway, way to go. How long have you kept this knowledge for? :p

Yuppy, of course its understood as a general practice that you’d want to run the least amount of ‘checks’ eg. if ( condition )… but object detection is most common in JavaScript for the main reason that we’re dealing with multiple browsers. Other dynamic languages like PHP are always ran on one server. The only case where it matters is if you wanted to make your code portable to different server configurations…eg. magic_quotes etc.

Anyway, for what it’s worth, it’s probably more common for JavaScripters to run into this problem than other languages.

What about testing for the presence of document.addEventListener and if it doesn’t exist assign functions to it that allow you to simulate it? So you’d do something like: (not tested)

if(!document.addEventListener) {
    if(document.attachEvent) {
        document.addEventListener = function(type, listener, useCapture) {
            this.attachEvent('on' + type, listener);
        };
    } else {
        document.addEventListener = new Function; // not supported
    }
}

That way you’d code and test according to the W3C DOM interface, but your code would still work with non-compliant browsers.

It could be moved into a separate library and re-used across multiple projects. (it would be interesting to know if anyone’s already tried this) Also, when most browsers eventually support the W3C DOM interface, you can just stop including the library; you won’t have to pull the functions out of the code.

  • Comment by: Dan Kubb
  • Posted:

Hey, great idea! With the popularity of Ajax bringing more js code into weblications, this is a nice technique. And I like that it’s more object oriented and less “linear” in style. Thanks for the tip.

Nice try Dan, but it’s not that easy. It’s not just document that needs to get the addEventListener method, but every element on the page. If I recall correctly, IE had problems with prototyping elements properly so this couldn’t really be done without iterating through every element (which still wouldn’t help for any elements created later). I’d be happy to be wrong about that, though.

Joshua – you are right. Dan’s code is flawed and will only work in very limited circumstances.

A note to Dan and others that post code here, please think through the code you post. I am considering deleting (or at least marking somehow) bad or misleading posts. You have been warned.

  • Comment by: -dean
  • Posted:

Great Idea!

Here is a more terse format:

addEvent=(
 document.addEventListener?(
    function(element, type, handler) {
        element.addEventListener(type, handler, null);
    }
 ):document.attachEvent?(
    function(element, type, handler) {
        element.attachEvent("on" + type, handler);
    }
 ): new Function; // not supported
)
  • Comment by: JavaScript Dude
  • Posted:

Javascript Resources Aaron Gustafson created a javascript tracing routine called jsTrace.  This is a new debugging technique…

I use similar techniques to write functions at runtime that unroll loops and eliminate many if/else checks for date formatting and parsing. Code that writes code has the potential to do amazing things. An initial performance hit can be repaid many times over by the gains in efficiency.

When you come from the AI world, the first thing you do in any new programming language is look for the ability to create lambda functions and pass functions around like data… =) it’s intimidating at first, but you’ll never go back!

Well done Dean.

  • Comment by: Xaprb
  • Posted:

[…] Dean has started publishing brief, handy JavaScript tips. The first is entitled Speed Up Object Detection. For those situations where code forking is unavoidable, Dean shows us […]

Nice tip, Dean.

I find it surprising that “new Function” without parentheses works. I’d have go go back to the specification to find out why this is legal (or whether it really is legal.) I suggest that your code would be less surprising, and therefore cleaner with “new Function()”.

Even better, though, would be to use an empty function literal: “function(){}”. That way your three cases are parallel

hey guys the last comment was from THE david flanagan from the the Java fame! Damn you have cool visitors on your website !

I think this would be interesting to you guys:

Behaviour: Using CSS selectors to apply Javascript functionality http://lambda-the-ultimate.org/node/view/816

Nice collaboration. You might just write the redeclaration into your addEvent(). That way it’s self-contained, executed only if called, and best of all you’ve created a closure to trap a “this” reference for IE:

function addEvent(elem, type, handler) {
 if (elem.addEventListener) {
   addEvent = function (elem, type, handler) {
     elem.addEventListener(type, window[handler], false);
   }
 } else if (elem.attachEvent) {
   addEvent = function (elem, type, handler) {
     elem.attachEvent('on' + type, window[handler]);
   }
 } else {
   addEvent = function () {}
 }
 addEvent(elem, type, handler);
}
  • Comment by: Dan Obenhaus
  • Posted:

Try jsTracer for JavaScript debugging. I find its more intuitive, and has a much nicer GUI. It also has a stack trace viewer which has helped me solve some difficult problems.

http://jstracer.sourceforge.net/

  • Comment by: MarkE
  • Posted:

What the?

Was that link spam for an open source project?

I mean, yeah, Dean writes about JS stuff a lot, but how did this post or any of the comments relate to a JS debugger?

  • Comment by: Jeremy Dunck
  • Posted:

The debugger is a general tool but the jstracer concept looks useful for viewing event bubbling. Since hooking up events is what the post is about the debugger would be useful to make sure the addEvent calls are working ok.

Thanks for this advice. Maybe with some more of this my animosity with JS will disappear…:)

  • Comment by: Erde
  • Posted:

[…] Dean has started publishing brief, handy JavaScript tips. The first is entitled Speed Up Object Detection. For those situations where code forking is unavoidable, Dean shows us […]

I’ve been using both versions suggested here for a few months, but today have come up with some JS code that throws the error: “element.addEventListener is not a function”

All I’m trying to do is attach onMouseOver events to a bunch of areas on an imagemap… standard stuff.

Now, I don’t get the error when using the old-style addEvent function.

Here’s my code…

if (!document.getElementById) return false ;
// Attach mouseover and mouseout handlers to every area in the image map
var myMap = document.getElementById('m_map') ;
if (myMap) {
	var myAreas = myMap.getElementsByTagName('area') ;
	for (iArea in myAreas) {
		addEvent(myAreas[iArea], 'mouseover', stateHover, false) ;
	}
}

Any ideas??

  • Comment by: Ben Hunt
  • Posted:

@Ben – I think I see the problem. Change this line:

for (iArea in myAreas)

to this:

for (var iArea = 0; i < myAreas.length; i++)

and it should work.

  • Comment by: -dean
  • Posted:

I did not realize object detection was a major player in the CPU cycles club. Well, I am assuming it is seeing how you wrote an article specifically addressing it. Some ms performance data would seal the deal.

I know this is an old example, but the logic is a bit flawed, IMO.

Testing for the existence of addEventListener, etc as a property of document does not guarantee that the method will exist on any arbitrary element passed in.

A better example may be one which test for document.getElementById once rather than with each id-resolution call, because it is the same method of the same object being tested for each time.

  • Comment by: Matt Kruse
  • Posted:

Not really new this tip, but good and for the multiple times use of a script this is a functionable way to optimize the performance.

  • Comment by: Mario
  • Posted:

[…] Let’s look at another example—one that Dean Edwards touched on about a year ago when he shared a brief tip on how to speed up object detection and apply the same technique of branching we used in our asyncRequest function to the problem of attaching event listeners. […]

Just read this on the back of a recent Dustin post and yet again you’ve blown my mind with pure simple genius. Nice one

[…] интересную технику почти год назад в обсуждении ускорения детекции объектов давайте, применим ее в нашем примере функции […]

Why not use try / catch instead of if / else?

  • Comment by: Joao Rodrigues
  • Posted:

I give up! How the hell do I submit well-formed XHTML in this comments box?! It looked fine to me, and I even got rid of the line breaks from crappy Notepad wordwrapping, and the apostrophes…

I was going to submit a code listing, but I’ll just say that addEventListener is actually working on my version of IE7 (couldn’t find any news about it, unless I’ve missed the obvious on Google). It’s just that it ruins all this addEvent conditional stuff, even if I put a return true statement immediately after the function redefinition, because attachEvent is still triggered and it goes through the handler functions in a seemingly random order. This happens no matter what order they’re called via addEvent! When I remove the check for attachEvent, though, aEL seems fine, surprisingly. Apart from the three alert boxes I get when I’m testing it with the window load event… Oh well, that’s IE.

  • Comment by: Marc K
  • Posted:

Forget what I said about the return true; I was confusing the optimised version of addEvent from post #19 here with my own sloppy version. I’ll try playing with the functions though.

  • Comment by: Marc K
  • Posted:

Comments are closed.