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.
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.
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; };
true
for 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.UIEvent
interface and UIEventInit
dictionaryUIEvent
and UIEventInit
are defined in [[!UIEvents]].
partial interface UIEvent { readonly attribute InputDeviceCapabilities? sourceCapabilities; }; partial dictionary UIEventInit { InputDeviceCapabilities? sourceCapabilities = null; };
null
if 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 same
sourceCapabilities
.
For example, when a user lifts their finger off of a touchscreen, several
UIEvents may be generated including touchend
, mousedown
,
click
, and focus
. All of these events must
have the same sourceCapabilities
representing the touchscreen.
sourceCapabilities
of a resize
event will typically be null
.
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);
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.
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 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.