Pointer Events in JavaScript
Tags: javascript, webapi, beginners
Published: 2023-03-04 12:43:18

We know that browsers work with the help of JavaScript, i.e. every browser has a JavaScript engine to execute the code to present the final web page. When writing code for the Web, there are a large number of Web APIs available. Today, we will understand how the pointer capture API works. [Here](https://developer.mozilla.org/en-US/docs/Web/API) is the list of all the APIs you may be able to use while developing a web application.

## Introduction to Pointer Events

A decade ago, if you would have asked me to give an example of the pointer, I would have referred to the cursor pointer of a computer that is accessed through a mouse.


However, with the invention of touch-based devices and their support for the web, touch events were introduced as a part of web APIs. These touch events provided a similar behavior as that of the pointer event. Since then, many devices supported other types of pointing input devices, such as pen/stylus and touch surfaces, and extensions to the existing pointing device event models were needed.

According to the official documentation -

> Pointer events are DOM events that are fired for a pointing device. They are designed to create a single DOM event model to handle pointing input devices such as a mouse, pen/stylus or touch (such as one or more fingers).

The [_pointer_](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events#pointer) is a hardware-agnostic device that can target a specific set of screen coordinates. Thus, pointers can simplify creating web applications by providing a good user experience, regardless of the user's hardware.

### Types of pointer events

Pointer events contain the usual properties of mouse events (client coordinates, target element, button states, etc.) in addition to new properties for other forms of input: pressure, contact geometry, tilt, etc.

| Pointer event        | Similar mouse event |
| -------------------- | ------------------- |
| `pointerdown`        | `mousedown`         |
| `pointerup`          | `mouseup`           |
| `pointermove`        | `mousemove`         |
| `pointerover`        | `mouseover`         |
| `pointerout`         | `mouseout`          |
| `pointerenter`       | `mouseenter`        |
| `pointerleave`       | `mouseleave`        |
| `pointercancel`      | \-                  |
| `gotpointercapture`  | \-                  |
| `lostpointercapture` | \-                  |

In fact, the `PointerEvent` interface inherits all of the `MouseEvent` properties, thus facilitating the migration of content from mouse events to pointer events.

You can refer to [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events) to learn in detail about the above-mentioned events. In this blog, we are going to focus on one of the special features of pointer events.

## Pointer Capture Event

Pointer capture allows the events for a pointer to be retargeted to a particular element other than the normal hit test result of the pointer's location.

The idea is very simple but may seem quite odd at first, as nothing like that exists for any other event type. We will learn this concept with a simple example. When I learnt about pointer capture events using this example, I was amazed by the simplicity it provides to the end users! Thus, I wanted to share this with a wider audience😀.

I'm sure most of you would have watched youtube videos on your phone. While watching, if you wanted to seek a particular timestamp, all you need to do is simply drag the `red dot` on the timeline and move to the appropriate timestamp value. The next time you perform this activity, observe if you are always touching the `red dot`. Most of the time, you would have touched the `red dot` but moved away from this target element. That's exactly where **"Pointer Capture Event"** comes into the picture.

You can test the demo of pointer capture on [https://webapis-playground.vercel.app/demo/pointer-capture](https://webapis-playground.vercel.app/demo/pointer-capture)

I built this feature as one of my early contributions to the **open source software (OSS)**.

### Let's understand how the code works!

For the above example, I have used React and Tailwind. I have kept the `pointer event` functionality in a separate JavaScript module since the application is mainly focusing on Web APIs.

JavaScript File: [https://github.com/atapas/webapis-playground/blob/master/src/modules/apis/pointer-capture/index.ts](https://github.com/atapas/webapis-playground/blob/master/src/modules/apis/pointer-capture/index.ts)

React File: [https://github.com/atapas/webapis-playground/blob/master/src/modules/demos/pointer-capture/index.tsx](https://github.com/atapas/webapis-playground/blob/master/src/modules/demos/pointer-capture/index.tsx)

The main elements to focus the React file are:

```javascript
 <div
          id="gray-timeline"
          className="tw-h-5 tw-w-full tw-bg-gray-400 tw-mb-5 tw-flex tw-items-center tw-relative tw-cursor-pointer"
          style={{ touchAction: isCapture ? 'none' : 'auto' }}
          onPointerUp={e => run.onPointerUp(e, isCapture)}
          onPointerDown={e => run.onPointerDown(e, isCapture)}
          onPointerMove={run.onPointerMove}
        >
          <div
            id="red-timeline"
            className="tw-relative tw-bg-red-600 tw-h-full tw-rounded-r-full tw-w-7"
          >
            <div
              id="red-dot"
              className="tw-absolute tw-bg-red-700 tw-h-7 tw-w-7 tw-rounded-full tw--top-1 tw-left-4"
            ></div>
          </div>
```

The main div container with the `id="gray-timeline"` has 3 events attached:

1. `onPointerDown` to start capturing the pointer event
2. `onPointerMove` to accurately provide the motion of the captured event (`red dot`) even though the `touch pointer` is not within the target element
3. `onPointerUp` to stop capturing the pointer event

By adding the pointer events on the parent container, the `id="red-timeline"` and `id="red-dot"` would work smoothly to capture and end the pointer event's behavior.

I have also added a react state `isCapture`. This would ensure that the pointer event is "captured" only when `isCapture=true`. You can set this value to false by toggling the switch to "Turn off".

Moving on, let's understand the behavior defined through these attached events.

```javascript
let flag = false;

function onPointerDown(event: React.PointerEvent<Element>, isCapture: boolean) {
  const timeline = document.querySelector('#red-timeline') as HTMLElement;
  if (isCapture) {
    timeline.setPointerCapture(event.pointerId);
  }
  flag = true;
  onPointerMove(event);
}
```

I am using a global variable `flag` to identify if the pointer is under capture. In the `onPointerDown` function, we find the target element i.e. the `red-timeline` element which needs to be captured. Based on the `isCapture` state value, we start capturing the pointer within this element by invoking the `setPointerCapture`.

`setPointerCapture` takes one parameter which is the `pointerId` of the given pointer event.

```javascript
function onPointerMove(event: React.PointerEvent<Element>) {
  const timeline = document.querySelector('#red-timeline') as HTMLElement;
  if (flag) {
    const rect = timeline.getBoundingClientRect();
    timeline.style.width = `${event.clientX - rect.left}px`;
    const childTag = document.querySelector('#dot') as HTMLElement;
    childTag.style.left = `${event.clientX - rect.left - 20}px`;
  }
}
```

Once the `onPointerDown` is fired and the pointer event is captured, as I start moving the pointer across the screen, the `onPointerMove` event is fired. In this function, we are capturing the latest value of the pointer and set the style attributes of the timeline element. It is because of this function you are able to see a smooth transition of the red timeline getting updated along with the movement of the `red-dot`.

```javascript

function onPointerUp(event: React.PointerEvent<Element>, isCapture: boolean) {
  const timeline = document.querySelector('#red-timeline') as HTMLElement;
  if (isCapture) {
    timeline.releasePointerCapture(event.pointerId);
  }
  flag = false;
}
```

The `onPointerUp` event is fired when we stop moving the "red dot" and release the touch (or click). At this moment, we release the pointer capture event and fire `onPointerUp` function. Here, we invoke `releasePointerCapture` to stop capturing the event any further.

### Link to the demo application

You can test the demo of pointer capture on [https://webapis-playground.vercel.app/demo/pointer-capture](https://webapis-playground.vercel.app/demo/pointer-capture)

Check out the demo of other Web APIs at [https://webapis-playground.vercel.app/](https://webapis-playground.vercel.app/)

If you learnt something new today and found this article exciting, go ahead and give this post a like!

Feel free to reach out to me on \[Twitter\](https://twitter.com/supminn) if you have any queries.

Peace ✌️