Copyright © 2024 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
The features in this specification extend or modify those found in Pointer Events, a W3C Recommendation that describes events and related interfaces for handling hardware agnostic pointer input from devices including a mouse, pen, touchscreen, etc. For compatibility with existing mouse based content, this specification also describes a mapping to fire Mouse Events for other pointer device types.
This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
This specification is an update to [PointerEvents3]. It includes editorial clarifications and new features that facilitate more use cases.
This document was published by the Pointer Events Working Group as a Working Draft using the Recommendation track.
Publication as a Working Draft does not imply endorsement by W3C and its Members.
This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is governed by the 03 November 2023 W3C Process Document.
This section is non-normative.
Today, most [HTML] content is used with and/or designed for mouse input. Those that handle input in a custom manner typically code to [UIEVENTS] Mouse Events. Newer computing devices today, however, incorporate other forms of input, including touchscreens, pen input, etc. Event types have been proposed for handling each of these forms of input individually. However, that approach often incurs unnecessary duplication of logic and event handling overhead when adding support for a new input type. This often creates a compatibility problem when content is written with only one device type in mind. Additionally, for compatibility with existing mouse-based content, most user agents fire Mouse Events for all input types. This makes it ambiguous whether a Mouse Event represents an actual mouse device or is being produced from another input type for compatibility, which makes it hard to code to both device types simultaneously.
To reduce the cost of coding to multiple input types and also to help with the above described ambiguity with Mouse Events, this specification defines a more abstract form of input, called a pointer. A pointer can be any point of contact on the screen made by a mouse cursor, pen, touch (including multi-touch), or other pointing input device. This model makes it easier to write sites and applications that work well no matter what hardware the user has. For scenarios when device-specific handling is desired, this specification also defines properties for inspecting the device type which produced the event. The primary goal is to provide a single set of events and interfaces that allow for easier authoring for cross-device pointer input while still allowing for device-specific handling only when necessary for an augmented experience.
An additional key goal is to enable multi-threaded user agents to handle direct manipulation actions for panning and zooming (for instance, with a finger or stylus on a touchscreen), without blocking on script execution.
While this specification defines a unified event model for a variety of pointer inputs, this model does not cover other forms of input such as keyboards or keyboard-like interfaces (for instance, a screen reader or similar assistive technology running on a touchscreen-only device, which allows users sequential navigation through focusable controls and elements). While user agents might choose to also generate pointer events in response to these interfaces, this scenario is not covered in this specification.
In the first instance, authors are encouraged to provide equivalent functionality for all forms of input by responding to high-level events such as focus
, blur
and click
. However, when using low-level events (such as Pointer Events), authors are encouraged to ensure that all types of input are supported. In the case of keyboards and keyboard-like interfaces, this might require the addition of explicit keyboard event handling. See WCAG Guideline 2.1 Keyboard Accessible [WCAG22] for further details.
The events for handling generic pointer input look a lot like those for mouse: pointerdown
, pointermove
, pointerup
, pointerover
, pointerout
, etc. This facilitates easy content migration from Mouse Events to Pointer Events.
Pointer Events provide all the usual properties present in Mouse Events (client coordinates, target element, button states, etc.) in addition to new properties for other forms of input: pressure, contact geometry, tilt, etc. So authors can easily code to Pointer Events to share logic between different input types where it makes sense, and customize for a particular type of input only where necessary to get the best experience.
While Pointer Events are sourced from a variety of input devices, they are not defined as being generated from some other set of device-specific events. While possible and encouraged for compatibility, this spec does not require other device-specific events be supported (e.g. mouse events, touch events, etc.). A user agent could support pointer events without supporting any other device events. For compatibility with content written to mouse-specific events, this specification does provide an optional section describing how to generate compatibility mouse events based on pointer input from devices other than a mouse.
This specification does not provide any advice on the expected behavior of user agents that support both Touch Events (as defined in [TOUCH-EVENTS]) and Pointer Events. For more information on the relationship between these two specifications, see the Touch Events Community Group.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MAY, MUST, MUST NOT, OPTIONAL, and SHOULD in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
This section is non-normative.
The following are basic examples that demonstrate how some of the APIs in this specification might be used by authors. Further, more specific examples are provided in the relevant sections of this document.
/* Bind to either Pointer Events or traditional touch/mouse */
if (window.PointerEvent) {
// if Pointer Events are supported, only listen to pointer events
target.addEventListener("pointerdown", function(e) {
// if necessary, apply separate logic based on e.pointerType
// for different touch/pen/mouse behavior
...
});
...
} else {
// traditional touch/mouse event handlers
target.addEventListener('touchstart', function(e) {
// prevent compatibility mouse events and click
e.preventDefault();
...
});
...
target.addEventListener('mousedown', ...);
...
}
// additional event listeners for keyboard handling
...
window.addEventListener("pointerdown", detectInputType);
function detectInputType(event) {
switch(event.pointerType) {
case "mouse":
/* mouse input detected */
break;
case "pen":
/* pen/stylus input detected */
break;
case "touch":
/* touch input detected */
break;
default:
/* pointerType is empty (could not be detected)
or UA-specific custom type */
}
}
<div style="position:absolute; top:0px; left:0px; width:100px;height:100px;"></div>
<script>
window.addEventListener("pointerdown", checkPointerSize);
function checkPointerSize(event) {
event.target.style.width = event.width + "px";
event.target.style.height = event.height + "px";
}
</script>
const event1 = new PointerEvent("pointerover",
{ bubbles: true,
cancelable: true,
composed: true,
pointerId: 42,
pointerType: "pen",
clientX: 300,
clientY: 500
});
eventTarget.dispatchEvent(event1);
let pointerEventInitDict =
{
bubbles: true,
cancelable: true,
composed: true,
pointerId: 42,
pointerType: "pen",
clientX: 300,
clientY: 500,
};
const p1 = new PointerEvent("pointermove", pointerEventInitDict);
pointerEventInitDict.clientX += 10;
const p2 = new PointerEvent("pointermove", pointerEventInitDict);
pointerEventInitDict.coalescedEvents = [p1, p2];
const event2 = new PointerEvent("pointermove", pointerEventInitDict);
eventTarget.dispatchEvent(event2);
<div style="position:absolute; top:0px; left:0px; width:100px;height:100px;"></div>
<script>
window.addEventListener("pointerdown", assignPenColor);
window.addEventListener("pointermove", assignPenColor);
const colorMap = new Map();
function assignPenColor(event) {
const uniqueId = event.persistentDeviceId;
// Check if a unique Id exists.
if (uniqueId == 0) {
return;
}
// Check if a color has been assigned to the device.
if (map.has(uniqueId)) {
return;
}
// Assign a color to the device.
let newColor = getNewColor();
map.set(uniqueId, newColor);
return newColor;
}
function getNewColor() {
/* return some color value */
}
</script>
WebIDLdictionary PointerEventInit
: MouseEventInit {
long pointerId
= 0;
double width
= 1;
double height
= 1;
float pressure
= 0;
float tangentialPressure
= 0;
long tiltX
;
long tiltY
;
long twist
= 0;
double altitudeAngle
;
double azimuthAngle
;
DOMString pointerType
= "";
boolean isPrimary
= false;
long persistentDeviceId
= 0;
sequence<PointerEvent
> coalescedEvents
= [];
sequence<PointerEvent
> predictedEvents
= [];
};
[Exposed=Window]
interface PointerEvent
: MouseEvent {
constructor
(DOMString type, optional PointerEventInit
eventInitDict = {});
readonly attribute long pointerId
;
readonly attribute double width
;
readonly attribute double height
;
readonly attribute float pressure
;
readonly attribute float tangentialPressure
;
readonly attribute long tiltX
;
readonly attribute long tiltY
;
readonly attribute long twist
;
readonly attribute double altitudeAngle
;
readonly attribute double azimuthAngle
;
readonly attribute DOMString pointerType
;
readonly attribute boolean isPrimary
;
readonly attribute long persistentDeviceId
;
[SecureContext] sequence<PointerEvent
> getCoalescedEvents
();
sequence<PointerEvent
> getPredictedEvents
();
};
pointerId
A unique identifier for the pointer causing the event. User agents MAY reserve a generic pointerId
value of 0
or 1
for the primary mouse pointer. The pointerId
value of -1
MUST be reserved and used to indicate events that were generated by something other than a pointing device. For any other pointers, user agents are free to implement different strategies and approaches in how they assign a pointerId
value. However, all active pointers in the top-level browsing context (as defined by [HTML]) must be unique, and the identifier MUST NOT be influenced by any other top-level browsing context (i.e. one top-level browsing context cannot assume that the pointerId
of a pointer will be the same when the pointer moves outside of the browsing context and into another top-level browsing context). The user agent MAY recycle previously retired values for pointerId
from previous active pointers, or it MAY always reuse the same pointerId
for a particular pointing device (for instance, to uniquely identify particular pen/stylus inputs from a specific user in a multi-user collaborative application). However, in the latter case, to minimize the chance of fingerprinting and tracking across different pages or domains, the pointerId
MUST only be associated explicitly with that particular pointing device for the lifetime of the page / session, and a new randomized pointerId
MUST be chosen the next time that particular pointing device is used again in a new session.
The pointerId
selection algorithm is implementation specific. Therefore authors cannot assume values convey any particular meaning other than an identifier for the pointer that is unique from all other active pointers. As an example, user agents may simply assign a number, starting from 0
, to any active pointers, in the order that they become active — but these values are not guaranteed to be monotonically increasing.
width
The width (magnitude on the X axis), in CSS pixels (see [CSS21]), of the contact geometry of the pointer. This value MAY be updated on each event for a given pointer. For inputs that typically lack contact geometry (such as a traditional mouse), and in cases where the actual geometry of the input is not detected by the hardware, the user agent MUST return a default value of 1
.
height
The height (magnitude on the Y axis), in CSS pixels (see [CSS21]), of the contact geometry of the pointer. This value MAY be updated on each event for a given pointer. For inputs that typically lack contact geometry (such as a traditional mouse), and in cases where the actual geometry of the input is not detected by the hardware, the user agent MUST return a default value of 1
.
pressure
The normalized pressure of the pointer input in the range of [0,1]
, where 0
and 1
represent the minimum and maximum pressure the hardware is capable of detecting, respectively. For hardware and platforms that do not support pressure, the value MUST be 0.5
when in the active buttons state and 0
otherwise.
tangentialPressure
The normalized tangential pressure (also known as barrel pressure), typically set by an additional control (e.g. a finger wheel on an airbrush stylus), of the pointer input in the range of [-1,1]
, where 0
is the neutral position of the control. Note that some hardware may only support positive values in the range of [0,1]
. For hardware and platforms that do not support tangential pressure, the value MUST be 0
.
tiltX
The plane angle (in degrees, in the range of [-90,90]
) between the Y-Z plane and the plane containing both the transducer (e.g. pen/stylus) axis and the Y axis. A positive tiltX
is to the right, in the direction of increasing X values. tiltX
can be used along with tiltY
to represent the tilt away from the normal of a transducer with the digitizer. For hardware and platforms that do not report tilt or angle, the value MUST be 0
.
tiltX
.tiltY
The plane angle (in degrees, in the range of [-90,90]
) between the X-Z plane and the plane containing both the transducer (e.g. pen/stylus) axis and the X axis. A positive tiltY
is towards the user, in the direction of increasing Y values. tiltY
can be used along with tiltX
to represent the tilt away from the normal of a transducer with the digitizer. For hardware and platforms that do not report tilt or angle, the value MUST be 0
.
tiltY
.twist
The clockwise rotation (in degrees, in the range of [0,359]
) of a transducer (e.g. pen/stylus) around its own major axis. For hardware and platforms that do not report twist, the value MUST be 0
.
altitudeAngle
The altitude (in radians) of the transducer (e.g. pen/stylus), in the range [0,π/2]
— where 0
is parallel to the surface (X-Y plane), and π/2
is perpendicular to the surface. For hardware and platforms that do not report tilt or angle, the value MUST be π/2
.
altitudeAngle
is π/2
,
which positions the transducer as being perpendicular to the surface.
This differs from the Touch Events - Level 2 specification's
definition for the altitudeAngle
property, which has a default value of 0
.
altitudeAngle
of π/4
(45 degrees from the X-Y plane).azimuthAngle
The azimuth angle (in radians) of the transducer (e.g. pen/stylus), in the range [0, 2π]
— where 0
represents a transducer whose cap is pointing in the direction of increasing X values (point to "3 o'clock" if looking straight down) on the X-Y plane, and the values progressively increase when going clockwise (π/2
at "6 o'clock", π
at "9 o'clock", 3π/2
at "12 o'clock"). When the transducer is perfectly perpendicular to the surface (altitudeAngle
of π/2
), the value MUST be 0
. For hardware and platforms that do not report tilt or angle, the value MUST be 0
.
azimuthAngle
of π/6
("4 o'clock").pointerType
Indicates the device type that caused the event (mouse, pen, touch, etc.). If the user agent is to fire a pointer event for a mouse, pen/stylus, or touch input device, then the value of pointerType
MUST be according to the following table:
Pointer Device Type | pointerType Value |
---|---|
Mouse | mouse |
Pen / stylus | pen |
Touch contact | touch |
If the device type cannot be detected by the user agent, then the value MUST be an empty string. If the user agent supports pointer device types other than those listed above, the value of pointerType
SHOULD be vendor prefixed to avoid conflicting names for different types of devices. Future specifications MAY provide additional normative values for other device types.
pointerType
can be used. Also note that developers should include some form of default handling to cover user agents that may have implemented their own custom pointerType
values and for situations where pointerType
is simply an empty string.isPrimary
Indicates if the pointer represents the primary pointer of this pointer type.
persistentDeviceId
A unique identifier for the pointing device. If the hardware supports multiple pointers, pointer events generated from pointing devices MUST only get a persistentDeviceId
if those pointers are uniquely identifiable over the session. If the pointer is uniquely identifiable, the assigned persistentDeviceId
to that pointing device will remain constant for the remainder of the session. The persistentDeviceId
value of 0
MUST be reserved and used to indicate events whose generating device could not be identified. Like pointerId, to minimize the chance of fingerprinting and tracking across different pages or domains, the persistentDeviceId
MUST only be associated explicitly with that particular pointing device for the lifetime of the page / session, and a new randomized persistentDeviceId
MUST be chosen the next time that particular pointing device is used again in a new session.
persistentDeviceId
is not guaranteed to be available for all pointer events from a pointing device. For example, the device may not report its hardware id to the digitizer in time for pointerdown
to have a persistentDeviceId
. In such a case, the persistentDeviceId
may initially be 0
and change to a valid value.getCoalescedEvents()
A method that returns the list of coalesced events.
getPredictedEvents()
A method that returns the list of predicted events.
The PointerEventInit
dictionary is used by the PointerEvent
interface's constructor to provide a mechanism by which to construct untrusted (synthetic) pointer events. It inherits from the MouseEventInit
dictionary defined in [UIEVENTS]. See the examples for sample code demonstrating how to fire an untrusted pointer event.
The event constructing steps for PointerEvent
clones PointerEventInit
's coalescedEvents
to coalesced events list and
clones PointerEventInit
's predictedEvents
to predicted events list.
PointerEvent
interface inherits from MouseEvent
, defined in UI Events.
Also note the proposed extension in CSSOM View Module, which changes the various coordinate properties from long
to double
to allow for fractional coordinates. For user agents that already implement this proposed extension for
PointerEvent
, but not for regular MouseEvent
, there are additional requirements when it comes to
the click
, auxclick
, and contextmenu
events.In a multi-pointer (e.g. multi-touch) scenario, the isPrimary
property is used to identify a master pointer amongst the set of active pointers for each pointer type.
pointerType
) are considered primary. For example, a touch contact and a mouse cursor moved simultaneously will produce pointers that are both considered primary.false
for isPrimary
.To fire a pointer event named e means to fire an event named e using PointerEvent
whose attributes are set as defined in PointerEvent
Interface and Attributes and Default Actions.
If the event is not a gotpointercapture
, lostpointercapture
, click
, auxclick
or contextmenu
event, run the process pending pointer capture steps for this PointerEvent
.
The target object at which the event is fired is determined as follows:
Let targetDocument be target's node document [DOM].
If the event is pointerdown
, pointermove
, or pointerup
set active document for the event's pointerId
to targetDocument.
If the event is pointerdown
, the associated device is a direct manipulation device, and the target is an Element
,
then set pointer capture for this pointerId
to the target element as described in implicit pointer capture.
Before firing this event, the user agent SHOULD treat the target as if the pointing device has moved over it from the previousTarget for the purpose of ensuring event ordering [UIEVENTS]. If the needsOverEvent flag is set, a pointerover
event is needed even if the target element is the same.
Fire the event to the determined target.
Save the determined target as the previousTarget for the given pointer,
and reset the needsOverEvent flag to false
.
If the previousTarget at any point will no longer be connected [DOM],
update the previousTarget to the nearest still connected [DOM] parent
following the event path corresponding to dispatching events to the previousTarget,
and set the needsOverEvent flag to true
.
The bubbles
and cancelable
properties and the default actions for the event types defined in this specification appear in the following table. Details of each of these event types are provided in Pointer Event types.
Event Type | Bubbles | Cancelable | Default Action |
---|---|---|---|
pointerover |
Yes | Yes | None |
pointerenter |
No | No | None |
pointerdown |
Yes | Yes | Varies: when the pointer is primary, all default actions of the mousedown event
Canceling this event also prevents subsequent firing of compatibility mouse events. |
pointermove |
Yes | Yes | Varies: when the pointer is primary, all default actions of mousemove |
pointerrawupdate |
Yes | No | None |
pointerup |
Yes | Yes | Varies: when the pointer is primary, all default actions of mouseup |
pointercancel |
Yes | No | None |
pointerout |
Yes | Yes | None |
pointerleave |
No | No | None |
gotpointercapture |
Yes | No | None |
lostpointercapture |
Yes | No | None |
Viewport manipulations (panning and zooming) — generally, as a result of a direct manipulation interaction — are intentionally NOT a default action of pointer events, meaning that these behaviors (e.g. panning a page as a result of moving a finger on a touchscreen) cannot be suppressed by canceling a pointer event. Authors must instead use touch-action
to explicitly declare the direct manipulation behavior for a region of the document. Removing this dependency on the cancelation of events facilitates performance optimizations by the user agent.
For pointerenter
and pointerleave
events, the composed
[DOM] attribute SHOULD be false
; for all other pointer events in the table above, the attribute SHOULD be true
.
For all pointer events in the table above, the detail
[UIEVENTS] attribute SHOULD be 0.
fromElement
and toElement
in MouseEvents to support legacy content. We encourage those user agents to set the values of those (inherited) attributes in PointerEvents to null
to transition authors to the use of standardized alternates (i.e. target
and relatedTarget
).Similar to MouseEvent
relatedTarget
, the relatedTarget
should be initialized to the element whose bounds the pointer just left (in the case of a pointerover
or pointerenter
event) or the element whose bounds the pointer is entering (in the case of a pointerout
or pointerleave
). For other pointer events, this value will default to null. Note that when an element receives the pointer capture all the following events for that pointer are considered to be inside the boundary of the capturing element.
For gotpointercapture
and lostpointercapture
events, all the attributes except the ones defined in the table above should be the same as the Pointer Event that caused the user agent to run the process pending pointer capture steps and fire the gotpointercapture
and lostpointercapture
events.
The user agent MUST run the following steps when implicitly releasing pointer capture as well as when firing Pointer Events that are not gotpointercapture
or lostpointercapture
.
lostpointercapture
at the pointer capture target override node.
gotpointercapture
at the pending pointer capture target override.
As defined in the section for click
, auxclick
, and contextmenu
events, even after the lostpointercapture
event has been dispatched,
the corresponding click
, auxclick
or contextmenu
event, if any, would still be dispatched to the capturing target.
The user agent MUST suppress a pointer event stream when it detects that a pointer is unlikely to continue to produce events. Any of the following scenarios satisfy this condition (there MAY be additional scenarios):
touch-action
CSS property for details.
Other scenarios in which the user agent MAY suppress a pointer event stream include:
Methods for detecting any of these scenarios are out of scope for this specification.
The user agent MUST run the following steps to suppress a pointer event stream:
pointercancel
event.pointerout
event.pointerleave
event.Pointer Events include two complementary sets of attributes to express the orientation of a transducer relative to the X-Y plane: tiltX
/ tiltY
(introduced in the original Pointer Events specification), and azimuthAngle
/ altitudeAngle
(adopted from the Touch Events - Level 2 specification).
Depending on the specific hardware and platform, user agents will likely only receive one set of values for the transducer orientation relative to the screen plane — either tiltX
/ tiltY
or altitudeAngle
/ azimuthAngle
. User agents MUST use the following algorithm for converting these values.
When the user agent calculates tiltX
/ tiltY
from azimuthAngle
/ altitudeAngle
it SHOULD round the final integer values using Math.round [ECMASCRIPT] rules.
/* Converting between tiltX/tiltY and altitudeAngle/azimuthAngle */
function spherical2tilt(altitudeAngle, azimuthAngle) {
const radToDeg = 180/Math.PI;
let tiltXrad = 0;
let tiltYrad = 0;
if (altitudeAngle == 0) {
// the pen is in the X-Y plane
if (azimuthAngle == 0 || azimuthAngle == 2*Math.PI) {
// pen is on positive X axis
tiltXrad = Math.PI/2;
}
if (azimuthAngle == Math.PI/2) {
// pen is on positive Y axis
tiltYrad = Math.PI/2;
}
if (azimuthAngle == Math.PI) {
// pen is on negative X axis
tiltXrad = -Math.PI/2;
}
if (azimuthAngle == 3*Math.PI/2) {
// pen is on negative Y axis
tiltYrad = -Math.PI/2;
}
if (azimuthAngle>0 && azimuthAngle<Math.PI/2) {
tiltXrad = Math.PI/2;
tiltYrad = Math.PI/2;
}
if (azimuthAngle>Math.PI/2 && azimuthAngle<Math.PI) {
tiltXrad = -Math.PI/2;
tiltYrad = Math.PI/2;
}
if (azimuthAngle>Math.PI && azimuthAngle<3*Math.PI/2) {
tiltXrad = -Math.PI/2;
tiltYrad = -Math.PI/2;
}
if (azimuthAngle>3*Math.PI/2 && azimuthAngle<2*Math.PI) {
tiltXrad = Math.PI/2;
tiltYrad = -Math.PI/2;
}
}
if (altitudeAngle != 0) {
const tanAlt = Math.tan(altitudeAngle);
tiltXrad = Math.atan(Math.cos(azimuthAngle) / tanAlt);
tiltYrad = Math.atan(Math.sin(azimuthAngle) / tanAlt);
}
return {"tiltX":tiltXrad*radToDeg, "tiltY":tiltYrad*radToDeg};
}
function tilt2spherical(tiltX, tiltY) {
const tiltXrad = tiltX * Math.PI/180;
const tiltYrad = tiltY * Math.PI/180;
// calculate azimuth angle
let azimuthAngle = 0;
if (tiltX == 0) {
if (tiltY > 0) {
azimuthAngle = Math.PI/2;
}
else if (tiltY < 0) {
azimuthAngle = 3*Math.PI/2;
}
} else if (tiltY == 0) {
if (tiltX < 0) {
azimuthAngle = Math.PI;
}
} else if (Math.abs(tiltX) == 90 || Math.abs(tiltY) == 90) {
// not enough information to calculate azimuth
azimuthAngle = 0;
} else {
// Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
const tanX = Math.tan(tiltXrad);
const tanY = Math.tan(tiltYrad);
azimuthAngle = Math.atan2(tanY, tanX);
if (azimuthAngle < 0) {
azimuthAngle += 2*Math.PI;
}
}
// calculate altitude angle
let altitudeAngle = 0;
if (Math.abs(tiltX) == 90 || Math.abs(tiltY) == 90) {
altitudeAngle = 0
} else if (tiltX == 0) {
altitudeAngle = Math.PI/2 - Math.abs(tiltYrad);
} else if (tiltY == 0) {
altitudeAngle = Math.PI/2 - Math.abs(tiltXrad);
} else {
// Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
altitudeAngle = Math.atan(1.0/Math.sqrt(Math.pow(Math.tan(tiltXrad),2) + Math.pow(Math.tan(tiltYrad),2)));
}
return {"altitudeAngle":altitudeAngle, "azimuthAngle":azimuthAngle};
}
Below are the event types defined in this specification.
In the case of the primary pointer, these events (with the exception of gotpointercapture
and lostpointercapture
) may also fire compatibility mouse events.
The user agent MUST fire a pointer event named pointerover
when a pointing device is moved into the hit test boundaries of an element. Note that setPointerCapture()
or releasePointerCapture()
might have changed the hit test target. Also note that while a pointer is captured it is considered to be always inside the boundaries of the capturing element for the purpose of firing boundary events. The user agent MUST also fire this event prior to firing a pointerdown
event for devices that do not support hover (see pointerdown
).
The user agent MUST fire a pointer event named pointerenter
when a pointing device is moved into the hit test boundaries of an element or one of its descendants, including as a result of a pointerdown
event from a device that does not support hover (see pointerdown
). Note that setPointerCapture()
or releasePointerCapture()
might have changed the hit test target. Also note that while a pointer is captured it is considered to be always inside the boundaries of the capturing element for the purpose of firing boundary events. This event type is similar to pointerover
, but differs in that it does not bubble.
mouseenter
event described in [UIEVENTS], and the CSS :hover
pseudo-class described in [CSS21]. See also the pointerleave
event.The user agent MUST fire a pointer event named pointerdown
when a pointer enters the active buttons state. For mouse, this is when the device transitions from no buttons depressed to at least one button depressed. For touch, this is when physical contact is made with the digitizer. For pen, this is when the pen either makes physical contact with the digitizer without any button depressed, or transitions from no buttons depressed to at least one button depressed while hovering.
pointerdown
and pointerup
are not fired for all of the same circumstances as mousedown
and mouseup
. See chorded buttons for more information.For input devices that do not support hover, the user agent MUST also fire a pointer event named pointerover
followed by a pointer event named pointerenter
prior to dispatching the pointerdown
event.
pointerdown
event (if the isPrimary
property is true
). This sets the PREVENT MOUSE EVENT
flag on the pointer. Note, however, that this does not prevent the mouseover
, mouseenter
, mouseout
, or mouseleave
events from firing.The user agent MUST fire a pointer event named pointermove
when a pointer changes any properties that don't fire
pointerdown
or pointerup
events. This includes any changes to coordinates, pressure, tangential pressure,
tilt, twist, contact geometry (i.e. width
and height
) or chorded buttons.
User agents MAY delay dispatch of the pointermove
event (for instance, for performance reasons).
The coalesced events information will be exposed via the getCoalescedEvents()
method for the single dispatched pointermove
event.
The final coordinates of such events should be used for finding the target of the event.
The user agent MUST fire a pointer event
named pointerrawupdate
, and only do so within a secure context, when a pointer changes any properties that don't fire
pointerdown
or pointerup
events. See pointermove
event for a list of such properties.
In contrast with pointermove
, user agents SHOULD dispatch pointerrawupdate
events as soon as possible
and as frequently as the JavaScript can handle the events.
The target
of pointerrawupdate
events might be different from the pointermove
events
due to the fact that pointermove
events might get delayed or coalesced, and the final position of the event
which is used for finding the target
could be different from its coalesced events.
Note that if there is already another pointerrawupdate
with the same pointerId
that hasn't been dispatched
in the event loop, the
user agent MAY coalesce the new pointerrawupdate
with that event instead of creating a new task.
This may cause pointerrawupdate
to have coalesced events, and
they will all be delivered as coalesced events of one pointerrawupdate
event as soon as
the event is processed in the event loop.
See getCoalescedEvents()
for more information.
In terms of ordering of pointerrawupdate
and pointermove
,
if the user agent received an update from the platform that causes both pointerrawupdate
and pointermove
events,
then the user agent MUST dispatch the pointerrawupdate
event before the corresponding pointermove
.
Other than the target
, the concatenation of coalesced events lists of all dispatched pointerrawupdate
events
since the last pointermove
event is the same as the coalesced events of the next pointermove
event in terms of the other event attributes.
The attributes of pointerrawupdate
are mostly the same as pointermove
, with the exception of
cancelable
which MUST be false for pointerrawupdate
.
User agents SHOULD not fire compatibility mouse events for pointerrawupdate
.
pointerrawupdate
event might negatively impact the performance of the web page, depending on the implementation of the user agent.
For most use cases the other pointerevent types should suffice.
A pointerrawupdate
listener should only be added if JavaScript needs high frequency events and can handle them just as fast.
In these cases, there is probably no need to listen to other types of pointer events.The user agent MUST fire a pointer event named pointerup
when a pointer leaves the active buttons state. For mouse, this is when the device transitions from at least one button depressed to no buttons depressed. For touch, this is when physical contact is removed from the digitizer. For pen, this is when the pen is removed from the physical contact with the digitizer while no button is depressed, or transitions from at least one button depressed to no buttons depressed while hovering.
For input devices that do not support hover, the user agent MUST also fire a pointer event named pointerout
followed by a pointer event named pointerleave
after dispatching the pointerup
event.
All pointerup
events have a pressure
value of 0
.
The user agent MUST also implicitly release the pointer capture if the pointer is currently captured.
pointerdown
and pointerup
are not fired for all of the same circumstances as mousedown
and mouseup
. See chorded buttons for more information.The user agent MUST fire a pointer event named pointercancel
when it detects a scenario to suppress a pointer event stream.
The values of the following properties of the pointercancel
event MUST match the values of the last dispatched pointer event with the same pointerId
: width
, height
, pressure
, tangentialPressure
, tiltX
, tiltY
, twist
, altitudeAngle
, azimuthAngle
, pointerType
, isPrimary
, and the coordinates inherited from [UIEVENTS]. The coalescedEvents
and predictedEvents
lists in the pointercancel
event MUST be empty, and the event's cancelable
attribute MUST be false.
The user agent MUST fire a pointer event named pointerout
when any of the following occurs:
setPointerCapture()
or releasePointerCapture()
might have changed the hit test target and while a pointer is captured it is considered to be always inside the boundaries of the capturing element for the purpose of firing boundary events.pointerup
event for a device that does not support hover (see pointerup
).The user agent MUST fire a pointer event named pointerleave
when any of the following occurs:
setPointerCapture()
or releasePointerCapture()
might have changed the hit test target and while a pointer is captured it is considered to be always inside the boundaries of the capturing element for the purpose of firing boundary events.pointerup
event for a device that does not support hover (see pointerup
).This event type is similar to pointerout
, but differs in that it does not bubble and that it MUST not be fired until the pointing device has left the boundaries of the element and the boundaries of all of its descendants.
The user agent MUST fire a pointer event named gotpointercapture
when an element receives pointer capture. This event is fired at the element that is receiving pointer capture. Subsequent events for that pointer will be fired at this element. See the setting pointer capture and process pending pointer capture sections.
The user agent MUST fire a pointer event named lostpointercapture
after pointer capture is released for a pointer. This event MUST be fired prior to any subsequent events for the pointer after capture was released. This event is fired at the element from which pointer capture was removed. All subsequent events for the pointer except click
, auxclick
, and contextmenu
events follow normal hit testing mechanisms (out of scope for this specification) for determining the event target. See the releasing pointer capture, implicit release of pointer capture, and process pending pointer capture sections.
The following section describes extensions to the existing Element
interface to facilitate the setting and releasing of pointer capture.
WebIDLpartial interface Element {
undefined setPointerCapture
(long pointerId);
undefined releasePointerCapture
(long pointerId);
boolean hasPointerCapture
(long pointerId);
};
setPointerCapture()
Set pointer capture for the pointer identified by the argument pointerId
to the element on which this method is invoked. For subsequent events of the pointer, the capturing target will substitute the normal hit testing result as if the pointer is always over the capturing target, and they MUST always be targeted at this element until capture is released. The pointer MUST be in its active buttons state for this method to be effective, otherwise it fails silently. When the provided method's argument does not match any of the active pointers, throw a "NotFoundError
" DOMException
.
releasePointerCapture()
Release pointer capture for the pointer identified by the argument pointerId
from the element on which this method is invoked. Subsequent events for the pointer follow normal hit testing mechanisms (out of scope for this specification) for determining the event target. When the provided method's argument does not match any of the active pointers, throw a "NotFoundError
" DOMException
.
hasPointerCapture
Indicates whether the element on which this method is invoked has pointer capture for the pointer identified by the argument pointerId
. In particular, returns true
if the pending pointer capture target override for pointerId
is set to the element on which this method is invoked, and false
otherwise.
setPointerCapture()
, even though that element will not yet have received a gotpointercapture
event. As a result it can be useful for detecting implicit pointer capture from inside of a pointerdown
event listener.The following section describes extensions to the existing GlobalEventHandlers
mixin to facilitate the event handler registration.
WebIDLpartial interface mixin GlobalEventHandlers {
attribute EventHandler onpointerover
;
attribute EventHandler onpointerenter
;
attribute EventHandler onpointerdown
;
attribute EventHandler onpointermove
;
[SecureContext] attribute EventHandler onpointerrawupdate
;
attribute EventHandler onpointerup
;
attribute EventHandler onpointercancel
;
attribute EventHandler onpointerout
;
attribute EventHandler onpointerleave
;
attribute EventHandler ongotpointercapture
;
attribute EventHandler onlostpointercapture
;
};
onpointerover
pointerover
event type.
onpointerenter
pointerenter
event type.
onpointerdown
pointerdown
event type.
onpointermove
pointermove
event type.
onpointerrawupdate
pointerrawupdate
event type.
onpointerup
pointerup
event type.
onpointercancel
pointercancel
event type.
onpointerout
pointerout
event type.
onpointerleave
pointerleave
event type.
ongotpointercapture
gotpointercapture
event type.
onlostpointercapture
lostpointercapture
event type.
As noted in Attributes and Default Actions, viewport manipulations (panning and zooming) cannot be suppressed by canceling a pointer event. Instead, authors must declaratively define which of these behaviors they want to allow, and which they want to suppress, using the touch-action
CSS property.
touch-action
CSS property defined in this specification appears to refer only to touch inputs, it does in fact apply to all forms of pointer inputs that allow direct manipulation for panning and zooming.Name: | touch-action |
---|---|
Value: | auto | none | [ [ pan-x | pan-left | pan-right ] || [ pan-y | pan-up | pan-down ] ] | manipulation |
Initial: | auto |
Applies to: | all elements except: non-replaced inline elements, table rows, row groups, table columns, and column groups. |
Inherited: | no |
Percentages: | N/A |
Media: | visual |
Computed value: | Same as specified value. |
The touch-action
CSS property determines whether direct manipulation interactions (which are not limited to touch, despite the property's name) MAY trigger the user agent's panning and zooming behavior. See the section on touch-action
values.
Right before starting to pan or zoom, the user agent MUST suppress a pointer event stream if all of the following conditions are true:
pointerdown
event has been sent for the pointer, andpointerup
or pointercancel
event (following the above mentioned pointerdown
) has not yet been sent for the pointer.touch-action
does not apply/cascade through to embedded browsing contexts. For instance, even applying touch-action
to an <iframe>
won't have any effect on the behavior of direct manipulation interactions for panning and zooming within the <iframe>
itself.When a user interacts with an element using a direct manipulation pointer (such as touch or stylus on a touchscreen), the effect of that input is determined by the value of the touch-action
property, and the default direct manipulation behaviors of the element and its ancestors, as follows:
touch-action
if the behavior is allowed in the coordinate space of the element. Note that if CSS transforms have been applied, the element's coordinate space may differ from the screen coordinate in a way that affects the conformity here; for example, the X axis of an element rotated by 90 degrees with respect to the screen will be parallel to the Y-axis of the screen coordinate.touch-action
property of each element between the hit tested element and its nearest inclusive ancestor that is a scroll container (as defined in [CSS-OVERFLOW-3]).touch-action
property of each element between the hit tested element and the document
element of the top-level browsing context (as defined in [HTML]).touch-action
value will be ignored for the duration of the action. For instance, programmatically changing the touch-action
value for an element from auto
to none
as part of a pointerdown
handler script will not result in the user agent aborting or suppressing any of the pan or zoom behavior for that input for as long as that pointer is active.touch-action
values of pan-*
, once the user agent has determined whether to handle a gesture directly or not at the start of the gesture, a subsequent change in the direction of the same gesture SHOULD be ignored by the user agent for as long as that pointer is active. For instance, if an element has been set to touch-action: pan-y
(meaning that only vertical panning is handled by the user agent), and a touch gesture starts off horizontally, no vertical panning should occur if the user changes the direction of their gesture to be vertical while their finger is still touching the screen.touch-action
values of multiple concurrent pointers is out of scope for this specification.The touch-action
property covers direct manipulation behaviors related to viewport panning and zooming. Any additional user agent behaviors, such as text selection/highlighting, or activating links and form controls, MUST NOT be affected by this CSS property.
auto
or none
values, are out of scope for this specification.pan-y
), the axis cannot be changed during panning.touch-action
values common in implementations are defined in [COMPAT].touch-action
property only applies to elements that support both the CSS width
and height
properties (see [CSS21]). This restriction is designed to facilitate user agent optimizations for low-latency direct manipulation panning and zooming. For elements not supported by default, such as <span>
which is a non-replaced inline element, authors can set the display
CSS property to a value, such as block
, that supports width
and height
. Future specifications could extend this API to all elements.The direction-specific pan values are useful for customizing some overscroll behaviors.
For example, to implement a simple pull-to-refresh effect the document's touch-action
can be set to pan-x pan-down
whenever the scroll position is 0
and pan-x pan-y
otherwise.
This allows pointer event handlers to define the behavior for upward panning/scrolling that start from the top of the document.
The direction-specific pan values can also be used for composing a component that implements custom panning with pointer event handling within an element that scrolls natively (or vice-versa).
For example, an image carousel may use pan-y
to ensure it receives pointer events for any horizontal pan operations without interfering with vertical panning of the document.
When the carousel reaches its right-most extent, it may change its touch-action
to pan-y pan-right
so that a subsequent scroll operation beyond its extent can scroll the document within the viewport if possible.
It's not possible to change the behavior of a panning/scrolling operation while it is taking place.
auto
user agents typically add 300ms of delay before click
to allow for double-tap gestures to be handled. In these cases, explicitly setting touch-action: none
or touch-action: manipulation
will remove this delay. Note that the methods for determining a tap or double-tap gesture are out of scope for this specification.<div style="touch-action: none;">
This element receives pointer events for all direct manipulation interactions that otherwise lead to panning or zooming.
</div>
<div style="touch-action: pan-x;">
This element receives pointer events when not panning in the horizontal direction.
</div>
<div style="overflow: auto;">
<div style="touch-action: none;">
This element receives pointer events for all direct manipulation interactions that otherwise lead to panning or zooming.
</div>
<div>
Direct manipulation interactions on this element MAY be consumed for manipulating the parent.
</div>
</div>
<div style="overflow: auto;">
<div style="touch-action: pan-y;">
<div style="touch-action: pan-x;">
This element receives pointer events for all direct manipulation interactions because
it allows only horizontal panning yet an intermediate ancestor
(between it and the scrollable element) only allows vertical panning.
Therefore, no direct manipulation behaviors for panning/zooming are
handled by the user agent.
</div>
</div>
</div>
<div style="overflow: auto;">
<div style="touch-action: pan-y pan-left;">
<div style="touch-action: pan-x;">
This element receives pointer events when not panning to the left.
</div>
</div>
</div>
This section is non-normative.
Pointer capture allows the events for a particular pointer (including any compatibility mouse events) to be retargeted to a particular element other than the normal hit test result of the pointer's location. This is useful in scenarios like a custom slider control (e.g. similar to the [HTML] <input type="range">
control). Pointer capture can be set on the slider thumb element, allowing the user to slide the control back and forth even if the pointer slides off of the thumb.
pointerdown
on the thumb, pointer capture can be used to allow the user to slide the thumb even if the pointer drifts off of it.Pointer capture is set on an element of type Element
by calling the element.setPointerCapture(pointerId)
method.
When this method is invoked, the user agent MUST run the following steps:
pointerId
provided as the method's argument does not match any of the active pointers, then throw a "NotFoundError
" DOMException
.pointerId
.InvalidStateError
" DOMException
.pointerLockElement
),
throw an "InvalidStateError
" DOMException
.pointerId
, set the pending pointer capture target override to the Element
on which this method was invoked.Pointer capture is released on an element explicitly by calling the element.releasePointerCapture(pointerId)
method. When this method is called, the user agent MUST run the following steps:
pointerId
provided as the method's argument does not match any of the active pointers and these steps are not being invoked as a result of the implicit release of pointer capture, then throw a "NotFoundError
" DOMException
.hasPointerCapture
is false for the Element
with the specified pointerId
, then terminate these steps.pointerId
, clear the pending pointer capture target override, if set.Inputs that implement direct manipulation interactions for panning and zooming (such as touch or stylus on a touchscreen) SHOULD behave exactly as if setPointerCapture()
was called on the target element just before the invocation of any pointerdown
listeners. The hasPointerCapture
API may be used (eg. within any pointerdown
listener) to determine whether this has occurred. If releasePointerCapture()
is not called for the pointer before the next pointer event is fired, then a gotpointercapture
event will be dispatched to the target (as normal) indicating that capture is active.
Immediately after firing the pointerup
or pointercancel
events,
the user agent MUST clear the pending pointer capture target override
for the pointerId
of the pointerup
or pointercancel
event that was just dispatched,
and then run process pending pointer capture steps to fire lostpointercapture
if necessary.
After running process pending pointer capture steps,
if the pointer supports hover, user agent MUST also send corresponding boundary events necessary
to reflect the current position of the pointer with no capture.
When the pointer capture target override is no longer connected [DOM], the pointer capture target override SHOULD be set to the document.
When the pending pointer capture target override is no longer connected [DOM], the pending pointer capture target override node SHOULD be cleared.
lostpointercapture
event
corresponding to the captured pointer being fired at the document
during the next Process pending pointer capture after the capture node is removed.
When a pointer lock [PointerLock] is successfully applied on an element, the user agent MUST run the steps as if the releasePointerCapture()
method has been called if any element is set to be captured or pending to be captured.
For performance reasons, user agents may choose not to send a pointermove
event every time a measurable property
(such as coordinates, pressure, tangential pressure, tilt, twist, or contact geometry)
of a pointer is updated. Instead, they may coalesce (combine/merge) multiple changes into
a single pointermove
or pointerrawupdate
event. While
this approach helps in reducing the amount of event handling the user agent must perform,
it will naturally reduce the granularity and fidelity when tracking a pointer position,
particularly for fast and large movements. Using the
getCoalescedEvents()
method
it is possible for applications to access the raw, un-coalesced position changes. These
allow for a more precise handling of pointer movement data. In the case of drawing
applications, for instance, the un-coalesced events can be used to draw smoother curves that
more closely match the actual movement of a pointer.
pointermove
events (the grey dots), the curve is noticeably
angular and jagged; the same line drawn using the more granular points provided by
getCoalescedEvents()
(the red circles) results in a smoother approximation
of the pointer movement.A PointerEvent
has an associated coalesced events list (a list of
zero or more PointerEvent
s). For trusted pointermove
and
pointerrawupdate
events, the list is a sequence of all PointerEvent
s
that were coalesced into this event. The "parent" trusted pointermove
and
pointerrawupdate
event represents an accumulation of these coalesced events,
but may have additional processing (for example to align with the display refresh rate).
As a result, the coalesced events lists for these events always contain at least one event.
For all other trusted event types, it is an empty list. Untrusted events have their
coalesced events list initialized to the value passed to the constructor.
The events in the coalesced events list of a trusted event will have:
timeStamp
values [DOM] — all coalesced events have a
timeStamp
that is smaller than or equal to the timeStamp
of the dispatched pointer event that the
getCoalescedEvents()
method was called on. The coalesced events list
MUST be chronologically sorted by timeStamp
, so the first event will have the smallest timeStamp
.pointerId
, pointerType
, and isPrimary
as the dispatched
"parent" pointer event.<style>
/* Disable intrinsic user agent direct manipulation behaviors (such as panning or zooming)
so that all events on the canvas element are given to the application instead. */
canvas { touch-action: none; }
</style>
<canvas id="drawSurface" width="500px" height="500px" style="border:1px solid black;"></canvas>
<script>
const canvas = document.getElementById("drawSurface"),
context = canvas.getContext("2d");
canvas.addEventListener("pointermove", (e)=> {
if (e.getCoalescedEvents) {
for (let coalesced_event of e.getCoalescedEvents()) {
paint(coalesced_event); // Paint all raw/non-coalesced points
}
} else {
paint(e); // Paint the final coalesced point
}
});
function paint(event) {
if (event.buttons>0) {
context.fillRect(event.clientX, event.clientY, 5, 5);
}
}
</script>
The order of all these dispatched events MUST match the actual order of the original events.
For example if a pointerdown
event causes the dispatch for the
coalesced pointermove
events the user agent MUST first dispatch one pointermove
event with all those coalesced events of a pointerId
followed by the pointerdown
event.
Here is an example of the actual events happening with increasing timeStamp
values and the
events dispatched by the user agent:
Actual events | Dispatched events |
---|---|
pointer (pointerId =2) coordinate change | pointerrawupdate (pointerId =2) w/ one coalesced event |
pointer (pointerId =1) coordinate change | pointerrawupdate (pointerId =1) w/ one coalesced event |
pointer (pointerId =2) coordinate change | pointerrawupdate (pointerId =2) w/ one coalesced event |
pointer (pointerId =2) coordinate change | pointerrawupdate (pointerId =2) w/ one coalesced event |
pointer (pointerId =1) coordinate change | pointerrawupdate (pointerId =1) w/ one coalesced event |
pointer (pointerId =2) coordinate change | pointerrawupdate (pointerId =2) w/ one coalesced event |
pointer (pointerId =1) button press |
pointermove (pointerId =1) w/ two coalesced eventspointermove (pointerId =2) w/ four coalesced eventspointerdown (pointerId =1) w/ zero coalesced events |
pointer (pointerId =2) coordinate change | pointerrawupdate (pointerId =2) w/ one coalesced event |
pointer (pointerId =2) coordinate change | pointerrawupdate (pointerId =2) w/ one coalesced event |
pointer (pointerId =1) button release |
pointermove (pointerId =2) w/ two coalesced eventspointerup (pointerId =1) w/ zero coalesced events |
Some user agents have built-in algorithms which, after a series of confirmed pointer movements,
can make a prediction (based on past points, and the speed/trajectory of the movement) what
the position of future pointer movements may be. Applications can use this information with
the getPredictedEvents()
method to speculatively "draw ahead" to a predicted position
to reduce perceived latency, and then discarding these predicted points once the actual points
are received.
pointermove
events, showing the user agent's predicted future points (the grey circles).A PointerEvent
has an associated predicted events list (a list of zero or more
PointerEvent
s). For trusted pointermove
events, it is a sequence of
PointerEvent
s that the user agent predicts will follow the event in the future.
For all other trusted event types, it is an empty list.
Untrusted events have their predicted events list initialized to the value passed to the
constructor.
While pointerrawupdate
events may have a non-empty coalesced events list,
their predicted events list will, for performance reasons, usually be an empty list.
The number of events in the list and how far they are from the current timestamp are determined by the user agent and the prediction algorithm it uses.
The events in the predicted events list of a trusted event will have:
timeStamp
values [DOM] — all predicted events have a
timeStamp
that is greater than or equal to the timeStamp
of the dispatched pointer event that the
getPredictedEvents()
method was called on. The predicted events list
MUST be chronologically sorted by timeStamp
, so the first event will have the smallest timeStamp
.pointerId
, pointerType
, and isPrimary
as the dispatched
"parent" pointer event.Note that authors should only consider predicted events as valid predictions until the next pointer event is dispatched. It is possible, depending on how far into the future the user agent predicts events, that regular pointer events are dispatched earlier than the timestamp of one or more of the predicted events.
let predicted_points = [];
window.addEventListener("pointermove", function(event) {
// Clear the previously drawn predicted points.
for (let e of predicted_points.reverse()) {
clearPoint(e.pageX, e.pageY);
}
// Draw the actual movements that happened since the last received event.
for (let e of event.getCoalescedEvents()) {
drawPoint(e.pageX, e.pageY);
}
// Draw the current predicted points to reduce the perception of latency.
predicted_points = event.getPredictedEvents();
for (let e of predicted_points) {
drawPoint(e.pageX, e.pageY);
}
});
When a trusted PointerEvent
is created, user agents SHOULD run the following steps for each event in the
coalesced events list and predicted events list:
pointerId
, pointerType
,
isPrimary
and isTrusted
to match the respective properties of the "parent" pointer event.cancelable
and bubbles
to false (as these events will never
be dispatched in isolation).PointerEvent
values.When a trusted PointerEvent
's target
is changed, user agents SHOULD, for each event in the
coalesced events list and predicted events list:
The vast majority of web content existing today codes only to Mouse Events. The following describes an algorithm for how the user agent MAY map generic pointer input to mouse events for compatibility with this content.
The compatibility mapping with mouse events is an OPTIONAL feature of this specification. User agents are encouraged to support the feature for best compatibility with existing legacy content.
At a high level, compatibility mouse events are intended to be "interleaved" with their respective pointer events. However, this specific order is not mandatory, and user agents that implement compatibility mouse events MAY decide to delay or group the dispatch of mouse events, as long as their relative order is consistent.
Particularly in the case of touchscreen inputs, user agents MAY apply additional heuristics for gesture recognition (unless explicitly suppressed by authors through
). During a sequence of events between a touch-action
pointerdown
event and a pointerup
event, the gesture recognition may have to wait until the pointerup
event to detect or ignore a gesture. As a result the compatibility mouse events for the whole sequence may be dispatched together after the last pointerup
event, if the user agent determined that an interaction was not intended as a particular gesture. These specifics of user agent gesture recognition are not defined in this specification, and they may differ between implementations.
Regardless of their support for compatibility mouse events, the user agents MUST always support the click
, auxclick
and contextmenu
events because these events are of type PointerEvent
and are therefore not compatibility mouse events. Calling preventDefault
during a pointer event MUST NOT have an effect on whether click
, auxclick
, or contextmenu
are fired or not.
The relative order of some of these high-level events (contextmenu
, focus
, blur
, etc.) with pointer events is undefined and varies between user agents. For example, in some user agents contextmenu
will often follow a pointerup
, while in others it'll often precede a pointerup
or pointercancel
, and in some situations it may be fired without any corresponding pointer event (for instance, as a result of a keyboard interaction).
In addition, user agents may apply their own heuristics to determine whether or not a click
, auxclick
, or contextmenu
event should be fired. Some user agents may choose not to fire these events if there are other (non-primary) pointers of the same type, or other primary pointers of a different type. User agents may determine that a particular action was not a "clean" tap, click, or long-press (for instance, if an interaction with a finger on a touch screen includes too much movement while the finger is in contact with the screen) and decide not to fire a click
, auxclick
, or contextmenu
event. These aspects of user agent behavior are not defined in this specification, and they may differ between implementations.
Unless otherwise noted, the target of any mapped mouse event SHOULD be the same target as the respective pointer event unless the target is no longer participating in its ownerDocument
's tree. In this case, the mouse event should be fired at the original target's nearest ancestor node (at the time it was removed from the tree) that still participates in its ownerDocument
's tree, meaning that a new event path (based on the new target node) is built for the mouse event.
Authors can prevent the production of certain compatibility mouse events by canceling the pointerdown
event.
Mouse events can only be prevented when the pointer is down. Hovering pointers (e.g. a mouse with no buttons pressed) cannot have their mouse events prevented.
The mouseover
, mouseout
, mouseenter
, and mouseleave
events are never prevented (even if the pointer is down).
Compatibility mouse events can't be prevented when a pointer event EventListener
is set to be passive
[DOM].
While only primary pointers can produce compatibility mouse events, multiple primary pointers can be active simultaneously, each producing its own compatibility mouse events. For compatibility with scripts relying on MouseEvents, the mouse transition events (mouseover
, mouseout
, mouseenter
and mouseleave
) SHOULD simulate the movement of a single legacy mouse input. This means that the entry/exit state for every event target is valid, in accordance with [UIEVENTS]. Users agents SHOULD guarantee this by maintaining the effective position of the legacy mouse pointer in the document as follows.
Right before firing a pointerdown
, pointerup
or pointermove
event, or a pointerleave
event at the window
, the user agent SHOULD run the following steps:
pointerdown
, pointerup
or pointermove
event being dispatched. For the pointerleave
event, unset T.mouseover
, mouseout
, mouseenter
and mouseleave
events as per [UIEVENTS] for a mouse moving from the current effective legacy mouse pointer position to T. Consider an unset value of either current effective legacy mouse pointer position or T as an out-of-window mouse position.The effective position of the legacy mouse pointer models the fact that we cannot always have a direct mapping
from pointer transition events (i.e., pointerover
, pointerout
, pointerenter
and pointerleave
) to corresponding legacy mouse transition events (i.e., mouseover
,
mouseout
, mouseenter
and mouseleave
). The following animation illustrates a case
where a user agent needs to dispatch more legacy mouse transition events than pointer transition events to be able to
reconcile two primary pointers using a single legacy mouse input.
In this animation, note the time period between the mouse click and the touch tap. Button 1 receives no
pointerout
event (because the "real" mouse pointer didn't leave the button rectangle within
this period), but Button 1 receives a mouseout
event when the effective position of the
legacy mouse pointer moves to Button 2 on touch tap. Similarly, in the time period between the touch
tap and the moment before the mouse leaves Button 1, Button 1 receives no pointerover
event
for the same reason, but Button 1 receives a mouseover
event when the effective position
of the legacy mouse pointer moves back inside Button 1.
Whenever the user agent is to dispatch a pointer event for a device that supports hover, it SHOULD run the following steps:
isPrimary
property for the pointer event to be dispatched is false
then dispatch the pointer event and terminate these steps.pointerdown
, pointerup
or pointermove
event, or a pointerleave
event at the window
, dispatch compatibility mouse transition events as described in Tracking the effective position of the legacy mouse pointer.pointerdown
and the event was canceled, then set the PREVENT MOUSE EVENT
flag for this pointerType
.PREVENT MOUSE EVENT
flag is not set for this pointerType
and the pointer event dispatched was:
pointerdown
, then fire a mousedown
event.pointermove
, then fire a mousemove
event.pointerup
, then fire a mouseup
event.pointercancel
, then fire a mouseup
event at the window
.pointerup
or pointercancel
, clear the PREVENT MOUSE EVENT
flag for this pointerType
.Some devices, such as most touchscreens, do not support hovering a coordinate (or set of coordinates) while not in the active state. Much existing content coded to mouse events assumes that a mouse is producing the events and thus certain qualities are generally true:
mousemove
event on an element before clicking it.This requires that user agents provide a different mapping for these types of input devices. Whenever the user agent is to dispatch a pointer event for a device that does not support hover, it SHOULD run the following steps:
isPrimary
property for the pointer event to be dispatched is false
then dispatch the pointer event and terminate these steps.pointerover
and the pointerdown
event has not yet been dispatched for this pointer, then fire a mousemove
event (for compatibility with legacy mouse-specific code).pointerdown
, pointerup
or pointermove
event, or a pointerleave
event at the window
, dispatch compatibility mouse transition events as described in Tracking the effective position of the legacy mouse pointer.pointerdown
and the event was canceled, then set the PREVENT MOUSE EVENT
flag for this pointerType
.PREVENT MOUSE EVENT
flag is not set for this pointerType
and the pointer event dispatched was:
pointerdown
, then fire a mousedown
event.pointermove
, then fire a mousemove
event.pointerup
, then fire a mouseup
event.pointercancel
, then fire a mouseup
event at the window
.pointerup
or pointercancel
, clear the PREVENT MOUSE EVENT
flag for this pointerType
.If the user agent supports both Touch Events (as defined in [TOUCH-EVENTS]) and Pointer Events, the user agent MUST NOT generate both the compatibility mouse events as described in this section, and the fallback mouse events outlined in [TOUCH-EVENTS].
The activation of an element (click
) with a primary pointer that does not support hover (e.g. single finger on a touchscreen) would typically produce the following event sequence:
mousemove
pointerover
pointerenter
mouseover
mouseenter
pointerdown
mousedown
pointermove
and mousemove
events, depending on movement of the pointerpointerup
mouseup
pointerout
pointerleave
mouseout
mouseleave
click
If, however, the pointerdown
event is canceled during this interaction then the sequence of events would be:
mousemove
pointerover
pointerenter
mouseover
mouseenter
pointerdown
pointermove
events, depending on movement of the pointerpointerup
pointerout
pointerleave
mouseout
mouseleave
click
This appendix discusses security and privacy considerations for Pointer Events implementations. The discussion is limited to security and privacy issues that arise directly from implementation of the event model, APIs and events defined in this specification.
Many of the event types defined in this specification are dispatched in response to user actions. This allows malicious event listeners to gain access to information users would typically consider confidential, e.g., the exact path/movement of a user's mouse/stylus/finger while interacting with a page.
Pointer events contain additional information (where supported by the user's device), such as the angle or tilt at which a pen input is held, the geometry of the contact surface, and the pressure exerted on the stylus or touch screen. Information about angle, tilt, geometry and pressure are directly related to sensors on the user's device, meaning that this specification allows an origin access to these sensors.
This sensor data, as well as the ability to determine the type of input mechanism (mouse, touch, pen) used, may be used to infer characteristics of a user, or of the user's device and environment. These inferred characteristics and any device/environment information may themselves be sensitive — for instance, they may allow a malicious site to further infer if a user is using assistive technologies. This information can also be potentially used for the purposes of building a user profile and/or attempting to "fingerprint" and track a particular user.
As mitigation, user agents may consider including the ability for users to disable access to particular sensor data (such as angle, tilt, pressure), and/or to make it available only after an explicit opt-in from the user.
Beyond these considerations, the working group believes that this specification:
This section is non-normative.
buttons
property. For mouse, this is when the device has at least one button depressed. For touch, this is when there is physical contact with the digitizer. For pen, this is when either the pen has physical contact with the digitizer, or at least one button is depressed while hovering.pointerId
) to produce additional events within the document, then that pointer is still considered active. Examples:
preventDefault()
, returning false
in an event handler, or other means as defined by [UIEVENTS] and [HTML].Measurable properties represent values relating to continuous pointer sensor data that is expressed using a real number or an
integer from a large domain. For pointer events, width
, height
, pressure
,
tangentialPressure
, tiltX
, tiltY
, twist
,
altitudeAngle
, azimuthAngle
, and the [UIEVENTS] Mouse Event model properties
screenX
, screenY
, clientX
, clientY
are measurable properties.
In contrast pointerId
, pointerType
, isPrimary
, and the
[UIEVENTS] Mouse Event model properties button
, buttons
, ctrlKey
,
shiftKey
, altKey
, and metaKey
are not considered measurable properties,
as they don't relate to sensor data.
Many thanks to lots of people for their proposals and recommendations, some of which are incorporated into this document. The group's Chair acknowledges contributions from the following past and present group members and participants: Mustaq Ahmed, Arthur Barstow, Ben Boyle, Matt Brubeck, Rick Byers, Marcos Cáceres, Cathy Chan, Bo Cupp, Domenic Denicola, Ted Dinklocker, Robert Flack, Dave Fleck, Mike Fraser, Ella Ge, Scott González, Kartikaya Gupta, Dominique Hazael-Massieux, Philippe Le Hégaret, Hayato Ito, Patrick Kettner, Patrick H. Lauke, Scott Low, Sangwhan Moon, Olli Pettay, Alan Pyne, Antoine Quint, Jacob Rossi, Kagami Sascha Rosylight, Doug Schepers, Ming-Chou Shih, Brenton Simpson, Dave Tapuska, Liviu Tinta, Asir Vedamuthu, Lan Wei, Navid Zolghadr
Special thanks to those that helped pioneer the first edition of this model, including especially: Charu Chandiram, Peter Freiling, Nathan Furtwangler, Thomas Olsen, Matt Rakow, Ramu Ramanathan, Justin Rogers, Jacob Rossi, Reed Townsend and Steve Wright.
This section is non-normative.
The following is an informative summary of substantial and major editorial changes between publications of this specification, relative to the [PointerEvents3] specification. See the complete revision history of the Editor's Drafts of this specification.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: