Detecting support for event types

R

RobG

When using createEvent, an eventType parameter must be provided as an
argument. This can be one of those specified in DOM 2 or 3 Events, or
it might be a proprietary eventType[1].

My problem is testing for support of particular eventTypes - the DOM 2
Events Interface DocumentEvent says that if the eventType is not
supported, it throws a DOM exception[2]. This makes testing rather
tough - if you try something like:

if (document && document.createEvent &&
document.createEvent(eventType)) {...}

or a typeof test and the eventType (say 'MouseEvents') isn't
supported, the script crashes with a DOM execption as specified in the
DOM 2 Events spec. I can do try..catch but I'd rather not if I can
avoid it:

var eventType = 'MouseEvents';
try {
document.createEvent(eventType);
alert(eventType + ' OK');
} catch (e) {
alert(eventType + ' not supported by createEvent');
return;
}


Is there a feature test that can be used?


1. Gecko supporte eventTypes:
<URL: http://developer.mozilla.org/en/docs/DOM:document.createEvent
2. W3C DOM 2 Events: Interface DocumentEvent - createEvent
<URL: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-DocumentEvent-createEvent
 
R

RobG

When using createEvent, an eventType parameter must be provided as an
argument.

While I'm at it, is there a list somewhere of the events that belong
to each event type (e.g. click, mouseup and mousedown belong to
MouseEvents while focus and blur belong to UIEvents), or do I have to
guess them and test by trial and error?
 
M

Martin Honnen

RobG said:
While I'm at it, is there a list somewhere of the events that belong
to each event type (e.g. click, mouseup and mousedown belong to
MouseEvents while focus and blur belong to UIEvents), or do I have to
guess them and test by trial and error?

The W3C DOM Level 2 Events specification lists events, for instance see
http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-UIEvent
then you will find DOMFocusIn, DOMFocusOut, and DOMActivate.
 
P

Peter Michaux

When using createEvent, an eventType parameter must be provided as an
argument.  This can be one of those specified in DOM 2 or 3 Events, or
it might be a proprietary eventType[1].

My problem is testing for support of particular eventTypes - the DOM 2
Events Interface DocumentEvent says that if the eventType is not
supported, it throws a DOM exception[2].  This makes testing rather
tough - if you try something like:

  if (document && document.createEvent &&
document.createEvent(eventType)) {...}

or a typeof test and the eventType (say 'MouseEvents') isn't
supported, the script crashes with a DOM execption as specified in the
DOM 2 Events spec.  I can do try..catch but I'd rather not if I can
avoid it:

  var eventType = 'MouseEvents';
  try {
    document.createEvent(eventType);
    alert(eventType + ' OK');
  } catch (e) {
    alert(eventType + ' not supported by createEvent');
    return;
  }

Is there a feature test that can be used?

Second paragraph here

<URL: http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mouseevents>

indicates something like the following would be sufficient

if (typeof document == 'object' && document &&
document.implementation &&
document.implementation.hasFeature &&
document.implementation.hasFeature('MouseEvents', 2.0))

But the browser may well still be lying as this implies the
MouseEvents interface is present *and* works. Evidence has shown that
many features are present in browsers but don't work. This may be too
paranoid.

See also Flanagan's 5th edition page 314 for a list of module names
that can be used for hasFeature, page 404 in the events section, and
page 776 in the reference section. (Flanagan's 4th edition has the
same content indexed.)

Peter
 
R

RobG

When using createEvent, an eventType parameter must be provided as an
argument. This can be one of those specified in DOM 2 or 3 Events, or
it might be a proprietary eventType[1].
My problem is testing for support of particular eventTypes - the DOM 2
Events Interface DocumentEvent says that if the eventType is not
supported, it throws a DOM exception[2]. This makes testing rather
tough - if you try something like:
if (document && document.createEvent &&
document.createEvent(eventType)) {...}
or a typeof test and the eventType (say 'MouseEvents') isn't
supported, the script crashes with a DOM execption as specified in the
DOM 2 Events spec. I can do try..catch but I'd rather not if I can
avoid it:
var eventType = 'MouseEvents';
try {
document.createEvent(eventType);
alert(eventType + ' OK');
} catch (e) {
alert(eventType + ' not supported by createEvent');
return;
}
Is there a feature test that can be used?

Second paragraph here

<URL:http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroup...>

indicates something like the following would be sufficient

if (typeof document == 'object' && document &&
document.implementation &&
document.implementation.hasFeature &&
document.implementation.hasFeature('MouseEvents', 2.0))

Looks familiar... :)

But the browser may well still be lying as this implies the
MouseEvents interface is present *and* works. Evidence has shown that
many features are present in browsers but don't work. This may be too
paranoid.

I don't think it's paranoid, just common sense. The W3C hasFeature is
no better than feature inference, it doesn't actually test for
support. It would have been much better (though still not sufficient)
if they'd specified hasFeature return null for unsupported eventTypes,
like getElementById does if there's no element with the specified id.

See also Flanagan's 5th edition page 314 for a list of module names
that can be used for hasFeature, page 404 in the events section, and
page 776 in the reference section. (Flanagan's 4th edition has the
same content indexed.)

Yeah, looked there. Following Martin's advice I created my own list
from various sources, here's what I have so far:

var allFeatures = {

Safari : ['TouchEvent', 'GestureEvent'],

Gecko : ['MessageEvent', 'MouseScrollEvents', 'PopupEvents',
'PopupBlockedEvents', 'XULCommandEvent',
'XULCommandEvents'],

SVG : ['SVGEvents', 'SVGEvent', 'SVGZoomEvents',
'SVGZoomEvent'],

DOM2 : ['UIEvents', 'MouseEvents', 'MutationEvents',
'HTMLEvents'],

DOM3 : ['UIEvent', 'MouseEvent', 'MutationEvent',
'MutationNameEvent', 'TextEvent', 'TextEvents',
'KeyboardEvent', 'KeyEvents', 'Event', 'Events']
};


and the supported events for each interface (wrapped for posting, not
all features added yet):

var ifaces = {
UIEvents : 'DOMFocusIn DOMFocusOut DOMActivate',

MouseEvents : 'click mousedown mouseup mouseover mousemove'
+ ' mouseout',

MutationEvent : 'DOMSubtreeModified DOMNodeInserted'
+ ' DOMNodeRemoved DOMNodeRemovedFromDocument'
+ ' DOMNodeInsertedIntoDocument DOMAttrModified'
+ ' DOMCharacterDataModified',

HTMLEvents : 'load unload abort error select change'
+ ' submit reset focus blur resize scroll',

// Draft events
TouchEvent : 'touchstart touchmove touchend touchcancel',

GestureEvent : 'gesturestart gesturechange gestureend'
};


I'm still working on the best way to organise them, I need to study
the W3C specs more. Perhaps I need to use hasFeature first, then
try..catch. The idea is to have a pre-calculated list of supported
event interfaces and types, then allow calling them by:

sendEvent( elementId, eventType [, parameterObject])

where each event type has a default parameter object that can be
modified by passing an object with just the parameters that need to be
set to some other value.

The event interface to use with createEvent is calculated from the
list of supported interfaces. If the appropriate interface isn't
supported, the event isn't dispatched.

All this started because I wanted to know a good way of detecting
support for touch events and expanded to be a more general way to send
any event into the DOM.

It seems Mozilla has changed its position on dispatchEvent. I'm
pretty sure that in Firefox 2 when a click event was fired on a link
using dispatchEvent, it follow the link, but Firefox 3 doesn't (nor
does IE using fireEvent), Safari 3 does. I haven't got to testing
other browsers but I expect this will only work in fairly modern
versions.

Maybe I'll go back to my original plan and do this just for touch
events.
 
P

Peter Michaux

[snip]
I'm still working on the best way to organise them, I need to study
the W3C specs more. Perhaps I need to use hasFeature first, then
try..catch.

What does that gain over just using try-catch? The only issue with try-
catch is it may syntax error in old browsers.
The idea is to have a pre-calculated list of supported
event interfaces and types, then allow calling them by:

sendEvent( elementId, eventType [, parameterObject])

where each event type has a default parameter object that can be
modified by passing an object with just the parameters that need to be
set to some other value.

This reads like prototype-based inheritance and I find it is rare to
actually find situations where prototype-based inheritance is so
appropriate.
The event interface to use with createEvent is calculated from the
list of supported interfaces. If the appropriate interface isn't
supported, the event isn't dispatched.

The way my feature testing has evolved would mean this "failure" is
too late. If the DOM has been manipulated and then depends on this
dispatch being successful and the dispatch will fail then the DOM
never should have been manipulated in the first place. In other words,
a widget isn't initialized unless it can be fully functional.

All this started because I wanted to know a good way of detecting
support for touch events

Apple's documentation is disappointingly dependent on
navigator.userAgent parsing.
and expanded to be a more general way to send
any event into the DOM.

I've never had any need for production code to send and even to a DOM
element. Do you?

Sending events to DOM elements could be useful in a test library.
Selenium does this but the code specific to each browser is a little
scary (admittedly they are writing something difficult.)
It seems Mozilla has changed its position on dispatchEvent. I'm
pretty sure that in Firefox 2 when a click event was fired on a link
using dispatchEvent, it follow the link, but Firefox 3 doesn't (nor
does IE using fireEvent), Safari 3 does. I haven't got to testing
other browsers but I expect this will only work in fairly modern
versions.

Maybe I'll go back to my original plan and do this just for touch
events.

What does something like
document.implementation.hasFeature('TouchEvent', 1.0) return?

Peter
 
T

Thomas 'PointedEars' Lahn

RobG said:
When using createEvent, an eventType parameter must be provided as an
argument. This can be one of those specified in DOM 2 or 3 Events, or
it might be a proprietary eventType[1].

My problem is testing for support of particular eventTypes - the DOM 2
Events Interface DocumentEvent says that if the eventType is not
supported, it throws a DOM exception[2]. This makes testing rather
tough - if you try something like:

if (document && document.createEvent &&
document.createEvent(eventType)) {...}

or a typeof test and the eventType (say 'MouseEvents') isn't
supported, the script crashes with a DOM execption as specified in the
DOM 2 Events spec.

It does _not_ really *crash* with the exception; the method *throws* the
(runtime) exception and you missed catching it.
I can do try..catch but I'd rather not if I can avoid it:

You cannot avoid it, this is exactly what the exception and consequently the
TryStatement with a Catch part is for. It has been learned from e.g. Java
that exceptions allow subroutines to return to their caller with a failure
state immediately, and exception handling allows the caller to recognize and
handle that state, without having to define a return value for each kind of
failure which would then no longer be available as return value for proper
execution.
var eventType = 'MouseEvents';
try {

You should add the "supposedly callable" feature test here, see below.
document.createEvent(eventType);

alert(eventType + ' OK');
} catch (e) {
alert(eventType + ' not supported by createEvent');
return;
}

Is there a feature test that can be used?

try...catch can be eval()'d to hide it from incompatible parsers:

/**
* Wrapper for safer <code>try</code>...<code>catch</code>.
*
* Attempts to evaluate a value as a <i>StatementList</i>, and attempts
* to evaluate another value as a <i>StatementList</i> if an exception
* is thrown in the process. The argument identifiers may be used
* in each value to refer to the used values; the <code>code</code>
* word may be used to the refer to the entire constructed
* <code>try</code>...<code>catch</code> string that is evaluated
* as a <i>Program</i>.
*
* @param statements
* Value to be evaluated as a <i>StatementList</i>.
* Called if a <code>Function</code> object reference, converted
* to string if not a string, and used as-is otherwise.
* For compatibility, the <code>undefined</code> value
* is evaluated like the empty string.
* @param errorHandlers
* Value to be evaluated as a <i>StatementList</i> in case of an
* exception. Called if a <code>Function</code> object reference,
* converted to string if not a string, and used as-is otherwise.
* For compatibility, the <code>undefined</code> value
* is evaluated like the empty string.
* @return
* The result of <code>statements</code>, or the result
* of <code>errorHandlers</code> if an error occurred.
* @author
* Copyright (c) 2008
* Thomas 'PointedEars' Lahn &lt;[email protected]&gt;
* Distributed under the GNU GPL v3 and later.
* @partof JSX:exception.js
*/
function tryThis(statements, errorHandlers)
{
/**
* @param s Value to be stringified
* @return Stringified version of <code>s</code>
*/
function stringify(s)
{
if (typeof s == "function")
{
s = "(" + s + ")()";
}
else if (typeof s == "undefined")
{
s = "";
}

return s;
}

statements = stringify(statements);
errorHandlers = stringify(errorHandlers);

var code = 'try { ' + statements + ' }'
+ 'catch (e) { ' + errorHandlers + ' }';

return eval(code);
}

var eventType = 'MouseEvents';
tryThis(
function() {
if (isMethod(document, "createEvent"))
{
document.createEvent(eventType);
window.alert(eventType + ' OK');
}
},
function() {
window.alert(eventType + ' not supported by createEvent');
});


HTH

PointedEars
 
R

RobG

[snip]
I'm still working on the best way to organise them, I need to study
the W3C specs more. Perhaps I need to use hasFeature first, then
try..catch.

What does that gain over just using try-catch?

If it returns false, don't even bother with the W3C event model.

I'm still not clear about the difference between the event model and
the Events *module* - does hasFeature('Events', '2.0') just mean
support for interface Event, or does it mean all of the interfaces for
the event model (Event, DocumentEvent, EventListener, EventTarget) but
not the extra ones like UIEvents, MutationEvents, etc.?

Anyhow, I figured hasFeaure might circumvent using try..catch on
interfaces where the module itself isn't supported, e.g. there are 7
interfaces in MutationEvent, if hasFeature(‘MutationEvents’,’2.0’)
returns false, that's 7 try..catch calls that can be avoided. I
figure it's more likely browsers will pretend to support modules than
not, or only implement part of them. Testing will reveal.

The only issue with try-
catch is it may syntax error in old browsers.

Yes, Thomas has posted a solution that I'll file away if it's really
needed.

The idea is to have a pre-calculated list of supported
event interfaces and types, then allow calling them by:
sendEvent( elementId, eventType [, parameterObject])
where each event type has a default parameter object that can be
modified by passing an object with just the parameters that need to be
set to some other value.

This reads like prototype-based inheritance and I find it is rare to
actually find situations where prototype-based inheritance is so
appropriate.

No, I have an isSupported method so you'd call:

if (isSupported('UIEvents')) {


The pre-calculated list is kept in isSupported using a closure and is
used so that the feature test doesn't have to happen every time.

The way my feature testing has evolved would mean this "failure" is
too late. If the DOM has been manipulated and then depends on this
dispatch being successful and the dispatch will fail then the DOM
never should have been manipulated in the first place. In other words,
a widget isn't initialized unless it can be fully functional.

It can use the same isSupported test.

Apple's documentation is disappointingly dependent on
navigator.userAgent parsing.


I've never had any need for production code to send and even to a DOM
element. Do you?

No, but although that isn't where I started it seems to be where I'm
headed. The goal is a feature test for touch events. One approach is
to attach an ontouchstart (or some other touch event) listener and
fire it using dispatchEvent it to see what happens - it seems very
crude but was a starting point. Along the way it occurred to me that
detecting support for the TouchEvent interface might be sufficient and
a little more elegant. Then I got a bit sidetracked into a more
general detection of event modules...

Sending events to DOM elements could be useful in a test library.
Selenium does this but the code specific to each browser is a little
scary (admittedly they are writing something difficult.)

I think I prefer the 'unobtrusive' approach - if it doesn't look like
it'll work, don't attempt it and fallback to simpler stuff.

What does something like
document.implementation.hasFeature('TouchEvent', 1.0) return?

I don't know, Apple's documentation doesn't provide any hints on the
version number or whether it should work with hasFeature - '1.0' is as
good a guess as any, I'll try it later. :)
 
R

RobG

I don't know, Apple's documentation doesn't provide any hints on the
version number or whether it should work with hasFeature - '1.0' is as
good a guess as any, I'll try it later.  :)

I tried all version numbers from '0.0' to '3.9', all returned false.
 
R

RobG

I tried all version numbers from '0.0' to '3.9', all returned false.

Here's my final effort:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>TouchEvent feature test</title>
<script type="text/javascript">

/* Feature tests for TouchEvent and GestureEvent modules
* implemented in Mobile Safari on iPhone
*
* The TouchEvent module is a draft (18/08/2008) that supports:
*
* touchstart
* touchmove
* touchend
* touchcancel
*
* The GestureEvent module is a draft (18/08/2008)
* that supports:
*
* gesturestart
* gesturechange
* gestureend
*
* The functions require support for try..catch, introduced
* in ECMA-262 ed3, JavaScript 1.4 and JScript 5.1.5010.
* Equates to Navigator 4.0 or later and IE 5.1 or later
* (http://pointedears.de/scripts/es-matrix/)
*
* If W3C DOM 2 Event document.createEvent is supported,
* return either true or false,
* otherwise return undefined.
*/
function touchSupported() {
if (document && document.createEvent) {
try {
document.createEvent('TouchEvent');
return true;
} catch (e) {
return false;
}
}
}

function gestureSupported() {
if (document && document.createEvent) {
try {
document.createEvent('GestureEvent');
return true;
} catch (e) {
return false;
}
}
}

/* Alternative hasTouch variable
*
* Example:
*
* if (hasTouches) {
* // assign touch and gesture events
* }
*/
var hasTouch = (function(){
if (document && document.createEvent) {
try {
document.createEvent('GestureEvent');
document.createEvent('TouchEvent');
return true;
} catch (e) {
return false;
}
}
})();
</script>
</head>
<body>
<div>
<button onclick="
alert('TouchEvent supported: ' + touchSupported());
">Touch supported</button>
<button onclick="
alert('GestureEvent supported: ' + gestureSupported());
">Gesture supported</button>
<button onclick="
alert('Touch and gestures supported: ' + hasTouch);
">Touch and gestures supported</button>
</div>
</body>
</html>
 
B

Beez

Yes, IE doesn't support createEvent.  Thanks for testing. :)

No problem! Is there an alternative to be had for IE? I looked --
the only thing remotely close that I could find is attachEvent or
fireEvent...neither one of which have the same effect.
 
D

dhtml

I like this type of approach better because it will have faster runtime
performance.

However, if document.createEvent is not supported, undefined is
returned. Why not just return true/false?
var hasTouch = (function(){
if (document && document.createEvent) {
try {
document.createEvent('GestureEvent');
document.createEvent('TouchEvent');
return true;
} catch (e) {
return false;
}
}
})();


Another possible approach would be to create variables for
touch/gesture, so as to avoid a runtime function call. Using the return
true/false approach, we could remove the conditional check altogether.

<!DOCTYPE HTML>
<html>
<head>
<title>EventSupport Demo</title>
<script type='text/javascript'>
var EventSupport = {};
(function(){
var es = EventSupport;
// Initialize the touch and gesture properties.
es.touch = hasSupport("TouchEvent");
es.gesture = hasSupport("GestureEvent");

function hasSupport(s){
try {
document.createEvent(s);
return true;
} catch (e) {
return false;
}
}
})();

</script>
</head>
<body>
<pre style="font-size:50px">
<script type='text/javascript'>
document.write(
'EventSupport.touch: ' + EventSupport.touch
+'\nEventSupport.gesture: ' + EventSupport.gesture
)
</script>
</pre>
</body>
</html>


Results:
iPhone 2.0:
EventSupport.touch: true
EventSupport.gesture: true

Firefox 3, et c.
EventSupport.touch: false
EventSupport.gesture: false


If other events were desired to be known, arbitrarily at runtime,
hasSupport could be added as a property of the EventSupport object.


Garrett
 
R

RobG

I like this type of approach better because it will have faster runtime
performance.

However, if document.createEvent is not supported, undefined is
returned. Why not just return true/false?

Because touch events might be supported through some other method, I
wanted to distinguish between no support for touch or gesture and no
support for the test itself.

Is that useful, or is true/false all that is required?

Another possible approach would be to create variables for
touch/gesture, so as to avoid a runtime function call.

Sure, my priority was a test that could be used in whatever manner was
appropriate. For example, when testing code there may be sections
based on mouseover or mouseout. Since touch interfaces don't fire
those events, it's nice to detect touch support and offer buttons to
fire the mouseover/out events if dispatchEvent is also supported.

My testing has been in Mobile Safari on iPhone and it works quite
well, it should be adaptable to other interfaces and perhaps detection
methods.

Other there other touch interfaces that have browsers? I'd be
interested to know how they've implemented DOM 0 events.

Using the return
true/false approach, we could remove the conditional check altogether. [...]
<script type='text/javascript'>
document.write(
'EventSupport.touch: ' + EventSupport.touch
+'\nEventSupport.gesture: ' + EventSupport.gesture
)
        </script>
</pre>
</body>
</html>

Results:
iPhone 2.0:
   EventSupport.touch: true
   EventSupport.gesture: true

Firefox 3, et c.
   EventSupport.touch: false
   EventSupport.gesture: false

If other events were desired to be known, arbitrarily at runtime,
hasSupport could be added as a property of the EventSupport object.

I implemented a test page that does almost exactly that :)
 
D

dhtml

sorry, that's not quite right. Nothing is returned, so the return value
is undefined.

Other there other touch interfaces that have browsers? I'd be
interested to know how they've implemented DOM 0 events.

Good question.


Garrett
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,479
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top