A Mouse, Some Touches, and All Sorts of Pointers

If you've ever tried to support both touch and mouse events on the web you know there is a lot to consider, and you can get tripped up - especially when you accept that it is not an either/or situation. Pointer Events try to unify different input pointing devices so you don't have to think as differently for touch and mouse (or a stylus... or anything new that comes like an airtap), while also giving you access to the details that might be unique to each type.

Pointer Events have been around since 2012 and the introduction of Windows 8 with IE 10. Having used them when I made Windows apps for the launch of the Windows Store, I've been excited about their potential. They abstract enough of the similarities between different pointer types while still providing pressure on a device that allows it.

With their introduction recently in Chrome 52 behind a flag, they now are on track to be in three stable evergreen browsers (with support already in Edge and Firefox (behind a flag since last August)). We'll take a look at how to use them, how to enable the flags, and how to provide a fallback to mouse + touch in other browsers.

Give Me the Basics, Please 

Pointer Events provide a single event for mouse + touch + stylus + other future pointing devices. They have common mappings to Mouse Events, such as if you currently listen to mousedown you would instead listen to pointerdown.

See the Pen Pointer Event Logging by Dan Wilson (@danwilson) on CodePen.

These events can be triggered: pointerdown, pointermove, pointerup, pointerenter, pointerleave, pointerout, pointerover, pointercancel

In addition to the normal event properties (such as pageX) when an event fires you also are given the following:

  • pointerId: a unique identifier (Integer) so that you can keep track of the touch with which you are dealing
  • pointerType: a string such as "mouse" or "touch"
  • pressure: a number between 0 and 1 for the pressure if the input and device support it (0 represents a hover, and 0.5 will be the default for an input + device that does not support pressure but is pressing)
  • tiltX: number in degress (from -90 to 90), visually described in the spec
  • tiltY: similar to tiltX, also visually described in the spec
  • width and height: dimensions (in CSS pixels) of the pointer. This will likely 1x1 for a mouse and something bigger for a touch, but is left to the browser to determine how to report it
  • button and buttons: shows which button is changed and all buttons pressed, respectively.

pointerId and pointerType are the two properties that will appear regardless of the capabilities of the pointer.

How Do I Use This Today? 

There are three pieces you will want to remember when using Pointer Events:

  1. To check support, see if window.PointerEvent is present.
  2. Listen to events as you would with other devices/input, such as Element.addEventListener('pointerdown', function(e) {})
  3. Set the touch-action CSS property to none (or other value than auto) on the element(s) that you want to receive Pointer Events.

It's the third point that needs the most explanation. If you do not set this in CSS, your pointer events will fire fine for mouse, but might do nothing on touch (as determined by each browser). This is because the browser likely already has a lot of touch gestures that perform actions at the browser level, such as scrolling and zooming the viewport. By setting touch-action: none you are telling the browser to not perform those browser-level actions on the element and instead trigger Pointer Events. There are other values that will allow you to continue some browser behaviors and then fire a subset of events.

See the Pen CSS Touch Action by Dan Wilson (@danwilson) on CodePen.

For example, you could still want normal scrolling/panning on a given element, but still want to know when a pointerdown occurs via a JS event. In that case you could set touch-action: pan. You will no longer receive pointermove events for every finger movement. You could even limit this to a direction such as touch-action: pan-y, which will fire pointermove events for horizontal moves, but perform the default browser behavior for vertical movements (with no JS events sent).

Edge will not fire any Pointer Events if touch-action is auto, while Chrome and Firefox both currently treat auto similar to pan. So it is best to know and specify what you want for consistency between browsers.

As of today, you can explore their uses in the following browsers

  • Edge (and IE 11 (and IE10 with a prefix)): Supported
  • Firefox (41+): Enable the dom.w3c_pointer_events.enabled and layout.css.touch_action.enabled flags in about:config
  • Chrome (52+): Enable the Pointer Events flag at chrome://flags/#enable-pointer-events

The fullest representation is Edge. In my testing Chrome and Firefox work well with a mouse or single touch point already, but have issues to resolve with multitouch.

What about Fallbacks? 

Almost everything in Pointer Events has a fallback either through Mouse Events or Touch Events. For example, Touch Events have an identifier that acts like pointerId and force which is similar to pressure.

I've not seen an equivalent for tilt*, so pen support will lack angles without Pointer Events. Though, it’s important to note tilt is only reported on a subset of pens (even the Surface Pro 4 pen does not support it in Edge).

We can use our support check to provide fallbacks

if (window.PointerEvent) {
//register event listeners for Pointer Events
} else {
//register for mouse and touch

Here is a full demo with multitouch and fallbacks. Press and the circles will surround your pointer. Release and they will fade away. If your pointer and device support it, more pressure/force will make the circles grow larger. 3D transforms occur when tilt values are detected.

See the Pen Press & Hold by Dan Wilson (@danwilson) on CodePen.

The caveats for this demo are the flagged releases of Chrome and Firefox. The Pointer events work well for mouse, pen, and a single touch point. When multiple touch points occur in Chrome and Firefox currently (flagged versions 52 and 48, respectively), it cannot keep up. Both browsers have work left before they become unflagged, and you can follow the status/bugs for both Chrome and Firefox for updates.