Friday, April 3rd, 2009
Detecting event support in browsers
<>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:-
-
var isEventSupported = (function(){
-
var TAGNAMES = {
-
'select':'input','change':'input',
-
'submit':'form','reset':'form',
-
'error':'img','load':'img','abort':'img'
-
}
-
function isEventSupported(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 isEventSupported;
-
})();
-
Along with this, he put up a simple test case that you can point at.
Related Content:











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’);
//…
}
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.
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
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?
@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.
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.
@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.
@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?
@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.
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;
})({});
Gosh!!!! cache[eventName] !== undefined …
@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.
@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.
@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.
@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!
@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 :)