Eventos de puntero

Eventos al interactuar con un dispositivo apuntador (ratón, touch, etc...)


Si necesitamos trabajar con acciones realizadas por el usuario con el ratón, generalmente optamos por MouseEvent. Sin embargo, esta API es más antigua y quizás nos pueda venir mejor otra API como TouchEvent o PointerEvent, esta última la que trataremos en este artículo.

Tipo de evento Descripción
MouseEvent Detecta acciones realizadas con un ratón o similar.
TouchEvent Detecta acciones realizadas en una pantalla táctil o similar.
PointerEvent Detecta acciones realizadas por un dispositivo apuntador (engloba los anteriores).

Aunque solemos generalizar y cuando trabajamos con Javascript hablamos de ratón, generalmente existen muchos tipos de dispositivos apuntadores: ratones, trackballs, lapiz óptico, touchpad, multitouch, etc...

A partir de ahora, en este artículo, hablaremos de puntero o apuntador para simplificar y referirnos a dichos dispositivos de forma generalizada.

El objeto PointerEvent

Veamos las propiedades principales que podemos tratar cuando estamos escuchando un evento de dispositivos apuntadores:

Propiedades Descripción
type Devuelve el tipo de evento, para identificarlo. En este caso: PointerEvent.
pointerId Devuelve un número, para identificar el dispositivo apuntador.
pointerType Identifica el tipo de puntero que estás utilizando: mouse, pen o touch.
width / height Ancho y alto (en píxeles) de la región pulsada.
isPrimary Indica si el dispositivo apuntador es el principal del sistema.
sourceCapabilities Indica las capacidades que tiene el dispositivo apuntador.
detail Generalmente, indica el número de clicks realizados.

En el siguiente apartado veremos un pequeño ejemplo utilizando alguna información de esta tabla.

Eventos genéricos

Eventos genéricos que hoy en día devuelven un PointerEvent serían los eventos click, auxclick y contextmenu. Aunque más adelante veremos otros eventos mucho más específicos para acciones del dispositivo apuntador, estos suelen ser bastante frecuentes para acciones sobre elementos de la UI de una página:

Evento Descripción
click Se ha activado el botón principal del dispositivo apuntador.
auxclick Se ha activado el botón secundario del dispositivo apuntador.
contextmenu Se ha desplegado el menú contextual (generalmente, con botón auxiliar).

Veamos un ejemplo simple:

const button = document.querySelector("button");
const status = document.querySelector(".status");

button.addEventListener("click", (ev) => {
  status.textContent =
    `Se ha disparado un evento ${ev.constructor.name} de tipo ${ev.pointerType}.`;
});
<div class="status"></div>
<button>Click me</button>

Valores especiales

La siguiente tabla de propiedades son valores numéricos que tienen relación con las inclinación o posición del apuntador (pen, stylus, etc...) o la presión con la que se pulsa:

Propiedades Descripción
Valores de pen/stylus
altitudeAngle Altitud (en radianes) del apuntador en el rango [0, PI/2]. Devuelve 0 si es paralelo a la superficie y PI/2 si es perpendicular o no tiene capacidad de detectar ángulo.
azimuthAngle Azimut (en radianes) del apuntador en el rango [0, PI/2]. Devuelve 0 si no se puede detectar ángulo.
twist La rotación [0, 359] del apuntador (pen, stylus). Devuelve 0 si no se puede detectar.
Valores de presión
pressure Presión del puntero entre [0, 1]. Si no soporta presión, devuelve 0.5.
tangentialPressure Presión del "barrel" entre [-1, 1]. Si no soporta, devuelve 0.

¿Qué botón se ha pulsado?

Una de las cosas que nos podría interesar saber es saber que botón ha sido pulsado. Esto se puede conocer facilmente con la propiedad button, que nos devuelve un valor numérico con el botón concreto con el que se ha pulsado y disparado el evento (por ejemplo, el evento pointerdown):

Propiedades Descripción
button Valor para indicar el estado del botón.
-1 Ningún botón/touch/pen ha cambiado desde el último evento.
0 Botón izquierdo de ratón o contacto con touch/pen
1 Botón medio del ratón
2 Botón derecho del ratón o "barrel" del pen
3 Botón X1 (atrás) del ratón
4 Botón X2 (adelante) del ratón
5 Botón de borrado del pen

Puedes comprobarlo en acción en este ejemplo. Prueba a pulsar sobre el área de color y te mostrará los valores de la propiedad button y la propiedad buttons:

const box = document.querySelector(".box");

document.body.addEventListener("pointerdown", (ev) => {
  const { button, buttons } = ev;

  box.innerHTML = `
    Valor de button: <code>${button}</code><br>
    Máscara de buttons: <code>${buttons}</code>
  `;
});
.box {
  width: 500px;
  height: 100px;
  background: indigo;
  font-family: Jost, sans-serif;
  font-size: 1.25rem;
  color: #fff;
  padding: 0.5rem;

  & code {
    color: gold;
  }
}
<div class="box">Pulsa aquí con diferentes botones</div>

Por otro lado, la propiedad buttons nos devuelve una máscara de bits. Esto puede ser más interesante si queremos saber si se han pulsado varios botones a la vez. Para ello, podemos hacer uso simplemento de operaciones de tipo ev.buttons & 4, para saber si se ha pulsado el botón del medio del apuntador de tipo ratón.

Los valores de la máscara son los siguientes:

Propiedades Descripción
buttons Máscara de bits para indicar el estado de los botones.
0 Mouse/pen movido sin presionar botones
1 Botón izquierdo de ratón o contacto con touch/pen
2 Botón derecho de ratón o "barrel" del pen
4 Botón medio del ratón
8 Botón X1 (atrás) del ratón
16 Botón X2 (adelante) del ratón
32 Botón de borrado del pen

La propiedad which que se usaba en el pasado para saber que botón ha sido pulsado ya es obsoleta y no debe utilizarse. En su lugar, lo recomendable es usar button o buttons.

Propiedades de posición

En algún caso, nos podría interesar obtener las posiciones o hacer cálculos con las distancias del apuntador respecto a otros elementos. Para ello, podemos utilizar las siguientes propiedades, que nos dan la posición donde se ha hecho click, touch o contacto.

Por ejemplo, clientX nos devuelve la posición horizontal en la parte visible del navegador, mientras que screenX nos devuelve la posición horizontal en el tamaño completo de pantalla. Así pues, tenemos algunas otras variaciones que podemos utilizar:

Propiedad Descripción
clientX / clientY Posición del apuntador en la región visible del navegador.
x / y Es un alias de las anteriores, clientX y clientY.
movementX / movementY Distancia recorrida por el apuntador desde el último evento.
offsetX / offsetY Posición del apuntador dentro del elemento target.
pageX / pageY Posición del apuntador en relación al documento.
screenX / screenY Posición del apuntado en relación a la pantalla del dispositivo.
tiltX / tiltY Inclinación del apuntador (si lo soporta).
layerX / layerY No utilizar.

Eventos de puntero

En el caso que necesitemos operaciones mucho más específicas del dispositivo apuntador, podemos utilizar los siguientes eventos:

Evento Descripción
pointerdown Se ha pulsado un botón del dispositivo apuntador en un elemento.
pointerup Se ha soltado un botón del dispositivo apuntador en un elemento.
pointermove Se ha movido un dispositivo apuntador sobre un elemento.
pointerover Igual al anterior, pero se propaga también a elementos secundarios.
pointerenter El dispositivo apuntador ha entrado en un elemento.
pointerleave El dispositivo apuntador ha salido de un elemento.
pointerout Igual al anterior, pero se propaga también a elementos secundarios.
pointerrawupdate Si necesitas alto nivel de refinado de eventos, puedes usar este evento.
pointercancel Se activa cuando es poco probable que hayan más eventos de puntero.

Eventos de captura de puntero

Existen algunos eventos muy interesantes mediante los cuales podemos capturar un puntero para realizar operaciones especiales. Por ejemplo, arrastrar un elemento de un lugar a otro:

Evento Descripción
gotpointercapture Se activa cuando se captura un puntero con .setPointerCapture().
lostpointercapture Se activa cuando se libera un puntero con .releasePointerCapture().

Para ello, podemos utilizar los métodos .setPointerCapture() y .releasePointerCapture() sobre un elemento del DOM en cuestión y escuchar los siguientes eventos de la tabla anterior.

const ball = document.querySelector(".ball");
let isDrag = false;

ball.addEventListener("pointerdown", ({ pointerId }) => {
  ball.setPointerCapture(pointerId);
  isDrag = true;
});

ball.addEventListener("pointerup", ({ pointerId }) => {
  ball.setReleaseCapture(pointerId);
  isDrag = false;
});

ball.addEventListener("pointermove", () => {
  if (isDrag) {
    /* ... */
  }
});

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev