Pointer Events

Level 4

W3C Working Draft

More details about this document
This version:
https://www.w3.org/TR/2025/WD-pointerevents4-20250501/
Latest published version:
https://www.w3.org/TR/pointerevents4/
Latest editor's draft:
https://w3c.github.io/pointerevents/
History:
https://www.w3.org/standards/history/pointerevents4/
Commit history
Test suite:
https://wpt.fyi/pointerevents/
Latest Recommendation:
https://www.w3.org/TR/pointerevents2
Editors:
Patrick H. Lauke (TetraLogical)
Robert Flack (Google)
Former editors:
Matt Brubeck (Mozilla)
Rick Byers (Google)
Navid Zolghadr (Google)
Feedback:
GitHub w3c/pointerevents (pull requests, new issue, open issues)
[email protected] with subject line [pointerevents4] … message topic … (archives)
Browser support:
caniuse.com

Abstract

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, or touchscreen. For compatibility with existing mouse-based content, this specification also describes a mapping to fire Mouse Events for other pointer device types.

Status of This Document

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 standards and drafts 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.

1. Introduction

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 and pen input. 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.

Note

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 Keyboard Accessible [WCAG22] for further details.

Pointer input combines various input sources such as mouse, pen, and touch
Figure 1 A pointer is a hardware-agnostic representation of input devices that can target a specific coordinate (or set of coordinates) on a screen.

The events for handling generic pointer input look a lot like those for mouse: pointerdown, pointermove, pointerup, pointerover, pointerout, and so on. This facilitates easy content migration from Mouse Events to Pointer Events. Pointer Events provide all the usual properties present in Mouse Events (including client coordinates, target element, button states) in addition to new properties for other forms of input, such as pressure, contact geometry, tilt. 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 (such as mouse events or touch events). 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.

Note

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.

2. Conformance

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.

3. Examples

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.

Example 1: Feature detection and event binding
/* 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
...
Example 2: Detecting the type of input from a user
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 */
    }
}
Example 3: Resizing an element to match the contact geometry
<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>
Example 4: Firing untrusted pointer events from 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);
Example 5: Assigning a pen color on PointerDown
<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>

4. Pointer Events and interfaces

4.1 PointerEvent interface

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.

Note

The pointerId selection algorithm is implementation specific. 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. As the reuse of the same pointerId for a particular pointing device is left up to individual implementations, authors are strongly discouraged from relying on it, and to refer to persistentDeviceId instead.

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.

Note
Despite the property's name, in practice the hardware controls/sensors that generate the values for this property may not necessarily be pressure sensitive. As an example, in most cases the finger wheel on most airbrush/painting stylus implementations can be freely set, rather than requiring the user to apply a constant pressure on the wheel to prevent it from returning to the zero position.
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 explanation diagram
Figure 2 Positive 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 explanation diagram
Figure 3 Positive 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.

Note
The default value defined here for 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 explanation diagram
Figure 4 Example 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 explanation diagram
Figure 5 Example azimuthAngle of π/6 ("4 o'clock").
pointerType

Indicates the device type that caused the event (such as mouse, pen, touch). 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 TypepointerType Value
Mousemouse
Pen / styluspen
Touch contacttouch

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.

Note
See Example 2 for a basic demonstration of how the 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.

Note
Due to digitizer and pointing device hardware constraints, a 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.

Note
The 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.

4.1.1 Button states

4.1.1.1 Chorded button interactions

Some pointer devices, such as mouse or pen, support multiple buttons. In the [UIEVENTS] Mouse Event model, each button press produces a mousedown and mouseup event. To better abstract this hardware difference and simplify cross-device input authoring, Pointer Events do not fire overlapping pointerdown and pointerup events for chorded button presses (depressing an additional button while another button on the pointer device is already depressed).

Instead, chorded button presses can be detected by inspecting changes to the button and buttons properties. The button and buttons properties are inherited from the MouseEvent interface, but with a change in semantics and values, as outlined in the following sections.

The modifications to the button and buttons properties apply only to pointer events. However for click, auxclick and contextmenu the value of button and buttons MUST follow [UIEVENTS], as is the case for compatibility mouse events .

4.1.1.2 The button property

To identify button state transitions in any pointer event (and not just pointerdown and pointerup), the button property indicates the device button whose state change fired the event.

Device Button Changesbutton
Neither buttons nor touch/pen contact changed since last event-1
Left Mouse,
Touch contact,
Pen contact
0
Middle Mouse1
Right Mouse,
Pen barrel button
2
X1 (back) Mouse3
X2 (forward) Mouse4
Pen eraser button5
Note
During a mouse drag, the value of the button property in a pointermove event will be different from that in a mousemove event. For example, while moving the mouse with the right button pressed, the pointermove events will have the button value -1, but the mousemove events will have the button value 2.
4.1.1.3 The buttons property

The buttons property gives the current state of the device buttons as a bitmask (same as in MouseEvent, but with an expanded set of possible values).

Current state of device buttonsbuttons
Mouse moved with no buttons pressed,
Pen moved while hovering with no buttons pressed
0
Left Mouse,
Touch contact,
Pen contact
1
Middle Mouse4
Right Mouse,
Pen barrel button
2
X1 (back) Mouse8
X2 (forward) Mouse16
Pen eraser button32

4.1.2 The primary pointer

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.

  • At any given time, there can only ever be at most one primary pointer for each pointer type.
  • The first pointer to become active for a particular pointer type (e.g. the first finger to touch the screen in a multi-touch interaction) becomes the primary pointer for that pointer type.
  • Only a primary pointer will produce compatibility mouse events. In the case where there are multiple primary pointers, these pointers will all produce compatibility mouse events.
Note
Authors who desire single-pointer interaction can achieve this by ignoring non-primary pointers (however, see the note below on multiple primary pointers).
Note
When two or more pointer device types are being used concurrently, multiple pointers (one for each pointerType) are considered primary. For example, a touch contact and a mouse cursor moved simultaneously will produce pointers that are both considered primary.
Note
Some devices, operating systems and user agents may ignore the concurrent use of more than one type of pointer input to avoid accidental interactions. For instance, devices that support both touch and pen interactions may ignore touch inputs while the pen is actively being used, to allow users to rest their hand on the touchscreen while using the pen (a feature commonly referred to as "palm rejection"). Currently, it is not possible for authors to suppress this behavior.
Note
In some cases, it is possible for the user agent to fire pointer events in which no pointer is marked as a primary pointer. For instance, when there are multiple active pointers of a particular type, like a multi-touch interaction, and the primary pointer is removed (e.g. it leaves the screen), there may end up being no primary pointers. Also on platforms where the primary pointer is determined using all active pointers of the same type on the device (including those targeted at an application other than the user agent), if the first (primary) pointer is outside of the user agent and other (non-primary) pointers targeted inside the user agent, then the user agent MAY fire pointer events for the other pointers with a value of false for isPrimary.
Note
Current operating systems and user agents don't usually have a concept of multiple mouse inputs. When more than one mouse device is present (for instance, on a laptop with both a trackpad and an external mouse), all mouse devices are generally treated as a single device — movements on any of the devices are translated to movement of a single mouse pointer, and there is no distinction between button presses on different mouse devices. For this reason, there will usually only be a single mouse pointer, and that pointer will be primary.

4.1.3 Firing events using the PointerEvent interface

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.

Determine the target at which the event is fired 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.

Note
Using the pointer capture target override as the target instead of the normal hit-test result may fire some boundary events, as defined by [UIEVENTS]. This is the same as the pointer leaving its previous target and entering this new capturing target. When the capture is released, the same scenario may happen, as the pointer is leaving the capturing target and entering the hit-test target.
4.1.3.1 Attributes and default actions

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 TypeBubblesCancelableDefault 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.

Note
Many user agents expose non-standard attributes 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 (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.

4.1.3.2 Process pending pointer capture

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.

  1. If the pointer capture target override for this pointer is set and is not equal to the pending pointer capture target override, then fire a pointer event named lostpointercapture at the pointer capture target override node.
  2. If the pending pointer capture target override for this pointer is set and is not equal to the pointer capture target override, then fire a pointer event named gotpointercapture at the pending pointer capture target override.
  3. Set the pointer capture target override to the pending pointer capture target override, if set. Otherwise, clear the pointer capture target override.
Note

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.

4.1.3.3 Suppressing a pointer event stream

The user agent MUST suppress a pointer event stream when it detects that the web page is unlikely to continue to receive pointer events with a specific pointerId. Any of the following scenarios satisfy this condition (there MAY be additional scenarios):

  • The user agent has opened a modal dialog or menu.
  • A pointer input device is physically disconnected, or a hoverable pointer input device (e.g. a hoverable pen/stylus) has left the hover range detectable by the digitizer.
  • The pointer is subsequently used by the user agent to manipulate the page viewport (e.g. panning or zooming). See the section on touch-action CSS property for details.
    Note
    User agents can trigger panning or zooming through multiple pointer types (such as touch and pen), and therefore the start of a pan or zoom action may result in the suppression of various pointers, including pointers with different pointer types.
  • As part of the drag operation initiation algorithm as defined in the drag and drop processing model [HTML], for the pointer that caused the drag operation.
Note

Other scenarios in which the user agent MAY suppress a pointer event stream include:

  • A device's screen orientation is changed while a pointer is active.
  • The user attempts to interact using more simultaneous pointer inputs than the device supports.
  • The user agent interprets the input as accidental (for example, the hardware supports palm rejection).

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:

4.1.4 Boundary events caused by layout changes

A pointing device that moved relative to the screen surface or underwent some change in any of its properties fires various events as defined in Pointer Event types. For a stationary pointing device (that neither moved relative to the screen surface nor underwent any change in any properties), the user agent MUST fire certain boundary events after a layout change that affected the hit test target for the pointer, see pointerover, pointerenter, pointerout and pointerleave for details. The user agent MAY delay the firing of these boundary events because of performance reasons (e.g. to avoid too many hit-tests or layout changes caused by boundary event listeners).

Note
A stationary pointing device (that neither moved relative to the screen surface nor underwent any change in any properties) never fires a pointermove event.

4.1.5 Converting between tiltX / tiltY and altitudeAngle / azimuthAngle

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.

Example 6: Converting between tiltX/tiltY and altitudeAngle/azimuthAngle
/* 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};
}

4.2 Pointer Event types

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.

4.2.1 The pointerover event

The user agent MUST fire a pointer event named pointerover when any of the following occurs:

4.2.2 The pointerenter event

The user agent MUST fire a pointer event named pointerenter when any of the following occurs:

Note
This event type is similar to pointerover but with two differences: pointerenter does not bubble, and its dispatch considers the hit test boundaries of even descendant elements.
Note
There are similarities between this event type, the mouseenter event described in [UIEVENTS], and the CSS :hover pseudo-class described in [CSS21]. See also the pointerleave event.

4.2.3 The pointerdown 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.

Note
For mouse (or other multi-button pointer devices), this means 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.

Note
Authors can prevent the firing of certain compatibility mouse events by canceling the 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.

4.2.4 The pointermove event

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 (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 thegetCoalescedEvents method for the single dispatched pointermove event. The final coordinates of such events should be used for finding the target of the event.

4.2.5 The pointerrawupdate 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. SeegetCoalescedEvents 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.

Note
Adding listeners for the 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.

4.2.6 The pointerup event

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.

Note
For mouse (or other multi-button pointer devices), this means pointerdown and pointerup are not fired for all of the same circumstances as mousedown and mouseup. See chorded buttons for more information.

4.2.7 The pointercancel event

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.

4.2.8 The pointerout event

The user agent MUST fire a pointer event named pointerout when any of the following occurs:

4.2.9 The pointerleave event

The user agent MUST fire a pointer event named pointerleave when any of the following occurs:

Note
This event type is similar to pointerout but with two differences: pointerleave does not bubble, and its dispatch considers the hit test boundaries of even descendant elements.
Note
There are similarities between this event type, the mouseleave event described in [UIEVENTS], and the CSS :hover pseudo-class described in [CSS21]. See also the pointerenter event.

4.2.10 The gotpointercapture event

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.

4.2.11 The lostpointercapture event

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.

4.2.12 The click, auxclick, and contextmenu events

This section is an addition to click, auxclick and contextmenu events defined in [UIEVENTS]. These events are typically tied to user interface activation, and are fired even from non-pointer input devices, such as keyboards.

These events MUST be of type PointerEvent, and are subject to the additional requirements mentioned in the rest of this section.

4.2.12.1 Event attributes

For these events, all PointerEvent specific attributes (defined in this spec) other than pointerId and pointerType MUST have their default values. In addition:

  • If the events are generated by a pointing device, their pointerId and pointerType MUST be the same as the PointerEvents that caused these events.
  • If the events are generated by a non-pointing device (such as voice recognition software or a keyboard interaction), pointerId MUST be -1 and pointerType MUST be an empty string.
4.2.12.2 Event coordinates

As noted in PointerEvent, CSSOM View Module proposes to redefine the various coordinate properties (screenX, screenY, pageX, pageY, clientX, clientY, x, y, offsetX, offsetY) as double, to allow for fractional coordinates. However, this change — when applied only to PointerEvent, but not to regular MouseEvent — has proven to lead to web compatibility issues with legacy code in the case of click, auxclick, and contextmenu. For this reason, user agents that have implemented the proposed change in CSSOM View Module only for PointerEvent MUST convert the various coordinate properties for the click, auxclick, and contextmenu to long values (as defined in the original UI Events) using Math.floor [ECMASCRIPT].

4.2.12.3 Event dispatch

A click, auxclick or contextmenu event MUST follow the dispatch process defined in the [UIEVENTS] spec except that the event target is overridden using the algorithm below:

  1. Let event be the click, auxclick or contextmenu event being dispatched, and userEvent be the user interaction event that caused the firing of event.

    Note

    Event userEvent could be a non-PointerEvent; for example, it is a KeyboardEvent when a click event dispatch is caused by hitting the spacebar on a checkbox element.

    When userEvent is a PointerEvent, userEvent is a pointerup for a click or auxclick event, and either a pointerdown or a pointerup event (depending on native platform convention) for a contextmenu event.

  2. If userEvent is not a PointerEvent, dispatch event following the [UIEVENTS] spec without overriding event target and skip the remaining steps below.
  3. Define target as follows:

    If event is a contextmenu event, or userEvent was dispatched while the corresponding pointer was captured, then let target be the target of userEvent.

    Otherwise (event is a click or auxclick event for which userEvent is a pointerup event that was dispatched uncaptured) let target be the nearest common inclusive ancestor of the corresponding pointerdown and pointerup targets in the DOM at the moment event is being dispatched.

  4. Dispatch event to target following the [UIEVENTS] spec.

    Note
    If userEvent was captured, event is dispatched to the capturing target of userEvent even though the lostpointercapture event with the same pointerId has been dispatched already.

5. Extensions to the Element interface

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.

Note
This method will return true immediately after a call to 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.

6. Extensions to the GlobalEventHandlers mixin

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
The event handler IDL attribute for the pointerover event type.
onpointerenter
The event handler IDL attribute for the pointerenter event type.
onpointerdown
The event handler IDL attribute for the pointerdown event type.
onpointermove
The event handler IDL attribute for the pointermove event type.
onpointerrawupdate
The event handler IDL attribute for the pointerrawupdate event type.
onpointerup
The event handler IDL attribute for the pointerup event type.
onpointercancel
The event handler IDL attribute for the pointercancel event type.
onpointerout
The event handler IDL attribute for the pointerout event type.
onpointerleave
The event handler IDL attribute for the pointerleave event type.
ongotpointercapture
The event handler IDL attribute for the gotpointercapture event type.
onlostpointercapture
The event handler IDL attribute for the lostpointercapture event type.

7. Extensions to the Navigator interface

The Navigator interface is defined in [HTML]. This specification extends the Navigator interface to provide device detection support.

WebIDLpartial interface Navigator {
    readonly  attribute long maxTouchPoints;
};
maxTouchPoints

The maximum number of simultaneous touch contacts supported by the device. In the case of devices with multiple digitizers (e.g. multiple touchscreens), the value MUST be the maximum of the set of maximum supported contacts by each individual digitizer.

For example, suppose a device has 3 touchscreens, which support 2, 5, and 10 simultaneous touch contacts, respectively. The value of maxTouchPoints should be 10.

Note
While a maxTouchPoints value of greater than 0 indicates the user's device is capable of supporting touch input, it does not necessarily mean the user will use touch input. Authors should be careful to also consider other input modalities that could be present on the system, such as mouse, pen, or screen readers.
Note
maxTouchPoints is often used to ensure that the interaction model of the content can be recognized by the current hardware. UI affordances can be provided to users with less capable hardware. On platforms where the precise number of touch points is not known, the minimum number guaranteed to be recognized is provided. Therefore, it is possible for the number of recognized touch points to exceed the value of maxTouchPoints.

8. Declaring direct manipulation behavior

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.

Note
While the issue of pointers used to manipulate the viewport is generally limited to touch input (where a user's finger can both interact with content and pan/zoom the page), certain user agents may also allow the same types of (direct or indirect) manipulation for other pointer types. For instance, on mobile/tablet devices, users may also be able to scroll using a stylus. While, for historical reasons, the 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.

8.1 The touch-action CSS property

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
Canonical order:per grammar
Animation type:not animatable

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:

Note
Some user agents implement complex gestures for behaviors that involve a series of separate discrete gestures, but which are all treated as part of a single continuous gesture. For example, consider a "fling to scroll" gesture on a touchscreen: a user starts panning the document with a rapid finger movement, lifts the finger from the touchscreen, and the document continues panning with simulated inertia. While the document is still moving, the user may place their finger on the touchscreen and execute another "fling" to provide further momentum for the panning, or counteract the current panning to slow it down, stop panning altogether, or reverse the direction of the panning. As this specification does not normatively define how gestures and behaviors are implemented, it is left up to the user agent to decide whether or not the second touch (before it is interpreted as a second "fling" or counteraction of the current panning) fires pointer events or not.
Note
touch-action does not apply/cascade through to embedded browsing contexts. For instance, even applying touch-action to an