| CARVIEW |
The APIs introduced by this document provide authors with a way to determine the capabilities of different input devices in response to DOM input events.
This proposal was produced in collaboration with members of the W3C Web Applications Working Group, the Pointer Events Working Group and the Touch Events Community Group.
Introduction
DOM input events are an abstraction above low-level input events, only loosely
tied to physical device input (e.g. click events can be fired by a mouse,
touchscreen or keyboard). There is no mechanism to obtain
lower-level details about the physical device responsible for an event.
Depending on implementation, certain types of input can also generate further
"fake" DOM input events for compatibility reasons. For example, touchscreen interactions
not only fire touch events, but also compatibility mouse events; when supporting both mouse and
touch input, it's difficult to know whether a mousedown event represents
new input from a mouse, or a compatibility event for a previously processed touchstart event.
This leads to problems, as developers may need to make assumptions or rely on heuristics.
The InputDeviceCapabilities API is designed to provide additional details about the underlying sources of input events. To avoid reliance on faulty assumptions (which may be violated by edge cases or future input devices), this API attempts to describe how a device behaves (e.g. whether it fires touch events), not what it is (e.g. a touchscreen). Initially this API is very minimal, focused on solving a specific problem with identifying mouse events derived from touch events, but it's expected that it will expand over time similar to other platforms.
The InputDeviceCapabilities interface
An InputDeviceCapabilities represents an input device (or group of related devices)
capable of generating input events. Events caused by the same physical input device
will get the same sourceCapabilities object, but the converse isn't necessarily true.
E.g. two mice with the same capabilities in a system may appear as a single
InputDeviceCapabilities instance.
In some cases an InputDeviceCapabilities will represent the capabilities of a logical
device rather than a physical input device. For example, in some environments keyboard events
are generated by tapping on an on-screen keyboard on a touchscreen. In such a
scenario the InputDeviceCapabilities represents the capabilities of the virtual keyboard
(since that's really what matters to authors), not the capabilities of the physical
touchscreen device (which should be considered an implementation detail of the logical
keyboard device).
[Exposed=Window]
interface InputDeviceCapabilities {
constructor(optional InputDeviceCapabilitiesInit deviceInitDict = {});
readonly attribute boolean firesTouchEvents;
readonly attribute boolean pointerMovementScrolls;
};
dictionary InputDeviceCapabilitiesInit {
boolean firesTouchEvents = false;
boolean pointerMovementScrolls = false;
};
- firesTouchEvents
-
Indicates whether this device dispatches [[Touch-Events]].
This property can be used to detect mouse events [[UIEvents]] which represent an action that may have already been handled by touch event handlers. See Example 1 for details. This doesn't necessarily mean the device is a touch screen. For example, stylus and mouse devices typically generate touch events on mobile browsers.
- pointerMovementScrolls
-
Indicates whether this device is a pointing device which typically triggers
scrolling when it is dragged across the content.
This property is typically
truefor direct-manipulation pointing devices (those where the physical metaphor is one of the user directly manipulating an object on the screen), such as touch screens and some on-screen stylus devices common on phones.
This property can be used, for example, in libraries which attempt to mimic native scrolling by consuming low-level input events. Such libraries typically respond to touch events but not mouse events. [[PointerEvents]] provide an abstraction between these devices (including devices like pens which may behave in either way depending on system convention). So when using the [[PointerEvents]] API it's best to explicitly check this capability rather than make assumptions about which types of events should trigger scrolling behavior.
Extensions to the UIEvent interface and UIEventInit dictionary
UIEvent and UIEventInit are defined in [[!UIEvents]].
partial interface UIEvent {
readonly attribute InputDeviceCapabilities? sourceCapabilities;
};
partial dictionary UIEventInit {
InputDeviceCapabilities? sourceCapabilities = null;
};
- sourceCapabilities
-
Indicates the InputDeviceCapabilities responsible for the generation
of this event, or
nullif no input device was responsible. When a single user interaction with an input device generates a series of different input events, all events in the series should have the samesourceCapabilities. For example, when a user lifts their finger off of a touchscreen, several UIEvents may be generated includingtouchend,mousedown,click, andfocus. All of these events must have the samesourceCapabilitiesrepresenting the touchscreen.A device is considered "responsible" for an event only when that interaction is part of the abstraction provided by the web platform. For example, many user agents allow a window to be resized with a mouse or a keyboard, but this detail is not exposed to the web platform in any way and so thesourceCapabilitiesof aresizeevent will typically benull.
Examples
The following are examples that demonstrate how the APIs in this specification might be used
myButton.addEventListener('touchstart', addHighlight);
myButton.addEventListener('touchend', removeHighlight);
// We don't call preventDefault because we want to rely on the browser's
// tap detection / 'click' to actually trigger the button.
myButton.addEventListener('mousedown', function(e) {
// Touch event case handled above, don't change the style again on tap
if (!e.sourceCapabilities.firesTouchEvents) {
addHighlight(e);
}
});
document.addEventListener('mouseup', function(e) {
if (!e.sourceCapabilities.firesTouchEvents) {
removeHighlight(e);
}
});
myButton.addEventListener('click', someFunction);
myCarousel.addEventListener('wheel', function(e) {
// Horizontal wheel movement always pans the carousel instead of scrolling.
if (e.deltaX > e.deltaY) {
panCarousel(e.deltaX);
e.preventDefault();
}
});
// Horizontal touch (or other direct-manipulation) input should
// also pan the carousel instead of scrolling.
myCarousel.style.touchAction='pan-y';
var lastX;
function onPointerEvent(e) {
if (e.isPrimary && e.sourceCapabilities.pointerMovementScrolls) {
if (e.type == 'pointermove')
panCarousel(e.clientX - lastX);
lastX = e.clientX;
}
};
myCarousel.addEventListener('pointedown', onPointerEvent);
myCarousel.addEventListener('pointermove', onPointerEvent);
Relationship to APIs in other platforms
Windows 8
Windows Runtime has a PointerDevice class, which is correlated to a pointer input event via Windows.UI.Input.PointerPoint.PointerDevice. Additional information is available from the MouseCapabilities, KeyboardCapabilities and TouchCapabilities classes. Note that this information is not available per-device.
At the low level, all WM_POINTER event messages provide access to a sourceDevice HANDLE for each point. Detailed per-device information can be obtained using the Raw Input APIs.
Mac OS X
MacOS X has various properties directly on NSEvent for determining device details. E.g. the mouse event sub-type, hasPreciseScrollingDeltas, and various properties (such as vendorPointingDeviceType) for tablet device information.
Android
Android has a rich InputDevice API similar to what’s being proposed here. Applications can determine the InputDevice for an event via InputEvent.getDevice, and can watch for changes in input devices using an InputDeviceManager.