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 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.
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 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.
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 the sourceCapabilities of a resize event will typically be null.

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.