Pointer events extend DOM input events to support various pointing input devices such as pen/stylus and touch screens as well as mouse. The pointer is a hardware-agnostic device that can target a specific set of screen coordinates. Having a single event model for pointers can simplify creating Web sites, applications and provide a good user experience regardless of the user's hardware.
Pointer events have many similarities to mouse events but they support multiple simultaneous pointers such as multiple fingers on a touch screen. This additional feature can be used to provide richer user interaction models but at the cost of additional complexity in the multi-touch interaction handling. This document demonstrates via example code, using pointer events with different multi-touch interactions.
A live version of this application is available on Github. The source code is available on Github; pull requests and bug reports are welcome.
Example
This example demonstrates using pointer events' various event types (pointerdown
, pointermove
, pointerup
pointercancel
, etc.) for different multi-touch interactions.
Define touch targets
The application uses <div>
to define three different touch target areas.
<style> div { margin: 0em; padding: 2em; } #target1 { background: white; border: 1px solid black; } #target2 { background: white; border: 1px solid black; } #target3 { background: white; border: 1px solid black; } </style>
Global state
To support multi-touch interaction, preserving a pointer's event state during various event phases is required. This application uses three arrays to cache event state, one cache per target element.
// Log events flag var logEvents = false; // Event caches, one per touch target var evCache1 = new Array(); var evCache2 = new Array(); var evCache3 = new Array();
Register event handlers
Event handlers are registered for the following pointer events: pointerdown
, pointermove
and pointerup
. The handler for pointerup
is used for the pointercancel
, pointerout
and pointerleave
events, since these four events have the same semantics in this application.
function set_handlers(name) { // Install event handlers for the given element var el=document.getElementById(name); el.onpointerdown = pointerdown_handler; el.onpointermove = pointermove_handler; // Use same handler for pointer{up,cancel,out,leave} events since // the semantics for these events - in this app - are the same. el.onpointerup = pointerup_handler; el.onpointercancel = pointerup_handler; el.onpointerout = pointerup_handler; el.onpointerleave = pointerup_handler; } function init() { set_handlers("target1"); set_handlers("target2"); set_handlers("target3"); }
Pointer down
The pointerdown
event is fired when a pointer (mouse, pen/stylus or touch point on a touchscreen) makes contact with the contact surface. The event's state must be cached, in case this down event is part of a multi-touch interaction.
In this application, when a pointer is placed down on an element, the background color of the element changes, depending on the number of active touch points the element has. See the update_background
function for more details about the color changes.
function pointerdown_handler(ev) { // The pointerdown event signals the start of a touch interaction. // Save this event for later processing (this could be part of a // multi-touch interaction) and update the background color push_event(ev); if (logEvents) log("pointerDown: name = " + ev.target.id, ev); update_background(ev); }
Pointer move
The pointermove
handler is called when the pointer moves. It may be called multiple times (for example, if the user moves the pointer) before a different event type is fired.
In this application, a pointer move is represented by the target's border being set to dashed
to provide a clear visual indication that the element has received this event.
function pointermove_handler(ev) { // Note: if the user makes more than one "simultaneous" touch, most browsers // fire at least one pointermove event and some will fire several pointermoves. // // This function sets the target element's border to "dashed" to visually // indicate the target received a move event. if (logEvents) log("pointerMove", ev); update_background(ev); ev.target.style.border = "dashed"; }
Pointer up
The pointerup
event is fired when a pointer is raised from the contact surface. When this occurs, the event is removed from the associated event cache.
In this application, this handler is also used for pointercancel
, pointerleave
and pointerout
events.
function pointerup_handler(ev) { if (logEvents) log(ev.type, ev); // Remove this touch point from the cache and reset the target's // background and border remove_event(ev); update_background(ev); ev.target.style.border = "1px solid black"; }
Application UI
The application uses <div>
elements for the touch areas and provides buttons to enable logging and to clear the log.
To prevent the browser's default touch behavior from overriding this application's pointer handling, the touch-action
property is applied to the <body>
element.
<body onload="init();" style="touch-action:none"> <div id="target1"> Tap, Hold or Swipe me 1</div> <div id="target2"> Tap, Hold or Swipe me 2</div> <div id="target3"> Tap, Hold or Swipe me 3</div> <!-- UI for logging/debugging --> <button id="log" onclick="enableLog(event);">Start/Stop event logging</button> <button id="clearlog" onclick="clearLog(event);">Clear the log</button> <p></p> <output></output> </body>
Miscellaneous functions
These functions support the application but aren't directly involved with the event flow.
Cache management
These functions manage the global event caches evCache1
, evCache2
and evCache3
.
function get_cache(ev) { // Return the cache for this event's target element switch(ev.target.id) { case "target1": return evCache1; case "target2": return evCache2; case "target3": return evCache3; default: log("Error with cache handling",ev); } } function push_event(ev) { // Save this event in the target's cache var cache = get_cache(ev); cache.push(ev); } function remove_event(ev) { // Remove this event from the target's cache var cache = get_cache(ev); for (var i = 0; i < cache.length; i++) { if (cache[i].pointerId == ev.pointerId) { cache.splice(i, 1); break; } } }
Update background color
The background color of the touch areas will change as follows: no active touches is white
; one active touch is yellow
; two simultaneous touches is ping
and three or more simultaneous touches is lightblue
.
function update_background(ev) { // Change background color based on the number of simultaneous touches/pointers // currently down: // white - target element has no touch points i.e. no pointers down // yellow - one pointer down // pink - two pointers down // lightblue - three or more pointers down var evCache = get_cache(ev); switch (evCache.length) { case 0: // Target element has no touch points ev.target.style.background = "white"; break; case 1: // Single touch point ev.target.style.background = "yellow"; break; case 2: // Two simultaneous touch points ev.target.style.background = "pink"; break; default: // Three or more simultaneous touches ev.target.style.background = "lightblue"; } }
Event logging
These functions are used send to event activity to the application window (to support debugging and learning about the event flow).
// Log events flag var logEvents = false; function enableLog(ev) { logEvents = logEvents ? false : true; } function log(name, ev) { var o = document.getElementsByTagName('output')[0]; var s = name + ": pointerID = " + ev.pointerId + " ; pointerType = " + ev.pointerType + " ; isPrimary = " + ev.isPrimary; o.innerHTML += s + " "; } function clearLog(event) { var o = document.getElementsByTagName('output')[0]; o.innerHTML = ""; }