Friday, April 3rd, 2009

Detecting event support in browsers

Category: JavaScript, Tip

<>p>Kangax has a really nice article on testing for event support in browsers in which he delves into the quirks and work-arounds needed to get ‘er done, coming up with a nice generic solution:

javascript
< view plain text >
  1. var isEventSupported = (function(){
  2.     var TAGNAMES = {
  3.       'select':'input','change':'input',
  4.       'submit':'form','reset':'form',
  5.       'error':'img','load':'img','abort':'img'
  6.     }
  7.     function isEventSupported(eventName) {
  8.       var el = document.createElement(TAGNAMES[eventName] || 'div');
  9.       eventName = 'on' + eventName;
  10.       var isSupported = (eventName in el);
  11.       if (!isSupported) {
  12.         el.setAttribute(eventName, 'return;');
  13.         isSupported = typeof el[eventName] == 'function';
  14.       }
  15.       el = null;
  16.       return isSupported;
  17.     }
  18.     return isEventSupported;
  19.   })();

Along with this, he put up a simple test case that you can point at.

Related Content:

Posted by Dion Almaer at 7:19 am
16 Comments

++++-
4.3 rating from 20 votes

16 Comments »

Comments feed TrackBack URI

Excellent work (as usual), kangax! Like many others, I couldn’t work-out a good cross-browser solution like this.

It’s too bad we have to create/destroy an element to do the check. I think I’d typically be working with an existing element when I wanted to determine event support (but not always, I guess). It looks pretty straightforward to extend this function to pass in an optional test element, rather than create one.

function isEventSupported(eventName, el) {
el = el || document.createElement(TAGNAMES[eventName] || ‘div’);
//…
}

Comment by unscriptable — April 3, 2009

This is really neat! I’ve noticed the function wrapper when I serialize the dom in TinyMCE but I never figured that it could be used for a feature detection. This would be very helpful to detect browser specific events like onmouseenter etc.

Comment by Spocke — April 3, 2009

Looks more like a isAvailable check. The existing property does not indicate that the event can be used in the browser, just see all the *buggy* and *incomplete* states in http://www.quirksmode.org/dom/events/index.html

Comment by digitarald — April 3, 2009

I was beaten to the punch, I was literally a week away from my own version of this very function. My solution utilizes the createEventObject and fireEvent methods for IE – which throws an error for unsupported events.

I never thought to use the “in” keyword for object inference to support events. Grea work!!! I may have to borrow some of these techniques for my release, I’ll give full credit of course.

The only problem is with those pesky Mutation events, could use document.implementation.hasFeature to detect it as a whole but I’d prefer something to detect the events individually.

Any ideas?

Comment by RyanMorr — April 3, 2009

@RyanMorr,
event names are enumerated on the native “Event” Object for Firefox, Safari and Konqueror. This allows to query for individual event names.

“MOUSEOVER” in Event; // will be true in those browsers

For IE and Opera no other way except using kangax proposal.

@kangax
thank you for sharing such useful informations.

Comment by dperini — April 3, 2009

Thanks everyone.

@unscriptable
You can definitely improvise with it :) My example only explains an idea.

@digitarald
Yes, `isAvailable` would probably be a better name. Unfortunately, testing for an actual event behavior is often too obtrusive (as I mentioned in the post).

@RyanMorr
Exactly which mutation events were you not able to feature test? Diego’s NWMatcher has great examples of some of the checks.

Comment by kangax — April 3, 2009

@kangax
Sorry I should have been more descriptive. I am aware of the ability to test for some of the mutation events. However I don’t like the idea of inferring support for one event simply because a similar one exists, for instance, assuming DOMContentLoaded is supported because DOMAttrModified is.

Comment by RyanMorr — April 3, 2009

@kangax
good stuff. About the name, isEventAvailable ? I am just concerned about this function expensiveness and I wonder if a simple cache implementation could make things better for everybody. Something like:

var isEventSupported = (function(cache){
var TAGNAMES = {
'select':'input','change':'input',
'submit':'form','reset':'form',
'error':'img','load':'img','abort':'img'
};
function isEventSupported(eventName) {
if(cache[eventName])
return cache[eventName];
var el = document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
var isSupported = (eventName in el);
if (!isSupported) {
el.setAttribute(eventName, 'return;');
isSupported = typeof el[eventName] == 'function';
};
el = null;
return isSupported;
}
return cache[eventName] = isEventSupported;
})({});

basically the same size, theoretically no side effects, performances improved. What do you think?

Comment by WebReflection — April 4, 2009

@RyanMorr,
you can test each one of the Mutation Events separately without inferring anything if you prefer so, as you said relying only on “.hasFeature()” may misrepresent real browser capabilities.

One example is FF2 were some of the Mutation Events exists and work well enough but are not shown through the “.implementation” interface.

Comment by dperini — April 4, 2009

oops, couple of typos (I should stop to write code in textareas :D)
var isEventSupported = (function(cache, undefined){
var TAGNAMES = {
'select':'input','change':'input',
'submit':'form','reset':'form',
'error':'img','load':'img','abort':'img'
};
function isEventSupported(eventName) {
if(cache[eventName] === undefined)
return cache[eventName];
var el = document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
var isSupported = (eventName in el);
if (!isSupported) {
el.setAttribute(eventName, 'return;');
isSupported = typeof el[eventName] == 'function';
};
el = null;
return cache[eventName] = isSupported;
};
return isEventSupported;
})({});

Comment by WebReflection — April 4, 2009

Gosh!!!! cache[eventName] !== undefined …

Comment by WebReflection — April 4, 2009

@dperini
True enough, I was able to test for almost all mutation events. I suppose I was just eluding to DOMContentLoaded, there is no reliable test for that considering it only fires once.

@WebReflection
I like the idea of caching, of course there needs to be some type of element or node inference, considering an onload event can fire on an image, iframe, or the window itself.

Comment by RyanMorr — April 4, 2009

@WebReflection

Good idea about caching – no need to recreate an element, set its attribute, etc. Btw, that’s an interesting way to check for `undefined` value you’ve got there. Is `typeof`not in style these days? : )

I would actually just use `in` operator (it would, of course, return `Object.prototype.*` properties, but so would `=== undefined` check; in any case, those are probably not important in this particular case) – `”eventName” in cache`

@RyanMorr
I’m not very familiar with DOMContentLoaded intrincancies, but shouldn’t it be possible to create a dummy context (i.e. iframe) and check if it fires on a document in that context?

Regarding association of events and element types, that’s an excellent point you raise there. Now that I think about it, an inference used in my original version could very well be too weak. A browser might be able to fire/listen for, say, “load” events on IFRAME’s but not on IMG’s. I wonder if there actually are browsers with such deficiencies out there.

Comment by kangax — April 4, 2009

@RyanMorr,
in newer W3C browser you can find a “DOMFrameContentLoaded” event which can be feature tested…and I believe it also makes more sense to have the controller of these events in a centralized location instead of inside each iframes, obviously if you have control over them.

Comment by dperini — April 4, 2009

@dperini
I’d heard of DOMFrameContentLoaded before, but again, although it is very likely that a browser that supports DOMFrameContentLoaded also supports DOMContentLoaded, it is an assumption. As I said in the last comment, there is only one place where detecting DOMContentLoaded would be necessary and that can already be achieved without browser detection.

Thanks for all the info, I honestly didn’t expect such a response. I’ll be sure to post a link to my solution once its finished (as part of a larger project) sometime this week. No doubt it will include many of the great ideas discussed here!

Comment by RyanMorr — April 5, 2009

@kangax, I wrote directly in the textarea without testing, that was just an idea and dunno why I used undefined but it is irrelevant since that is just a “somehow cache it” suggestion :)

Comment by WebReflection — April 6, 2009

Leave a comment

You must be logged in to post a comment.