Eventos de puntero

Eventos al interactuar con dispositivos apuntadores (ratón, touch...)


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 eventoDescripción
MouseEventDetecta acciones realizadas con un ratón o similar.
TouchEventDetecta acciones realizadas en una pantalla táctil o similar.
PointerEventDetecta 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:

PropiedadesDescripción
typeDevuelve el tipo de evento, para identificarlo. En este caso: PointerEvent.
pointerIdDevuelve un número, para identificar el dispositivo apuntador.
pointerTypeIdentifica el tipo de puntero que estás utilizando: mouse, pen o touch.
width / heightAncho y alto (en píxeles) de la región pulsada.
isPrimaryIndica si el dispositivo apuntador es el principal del sistema.
sourceCapabilitiesIndica las capacidades que tiene el dispositivo apuntador.
detailGeneralmente, 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:

EventoDescripción
clickSe ha activado el botón principal del dispositivo apuntador.
auxclickSe ha activado el botón secundario del dispositivo apuntador.
contextmenuSe 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:

PropiedadesDescripción
Valores de pen/stylus
altitudeAngleAltitud (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.
azimuthAngleAzimut (en radianes) del apuntador en el rango [0, PI/2]. Devuelve 0 si no se puede detectar ángulo.
twistLa rotación [0, 359] del apuntador (pen, stylus). Devuelve 0 si no se puede detectar.
Valores de presión
pressurePresión del puntero entre [0, 1]. Si no soporta presión, devuelve 0.5.
tangentialPressurePresió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):

PropiedadesDescripción
buttonValor para indicar el estado del botón.
-1Ningún botón/touch/pen ha cambiado desde el último evento.
0Botón izquierdo de ratón o contacto con touch/pen
1Botón medio del ratón
2Botón derecho del ratón o "barrel" del pen
3Botón X1 (atrás) del ratón
4Botón X2 (adelante) del ratón
5Botó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:

PropiedadesDescripción
buttonsMáscara de bits para indicar el estado de los botones.
0Mouse/pen movido sin presionar botones
1Botón izquierdo de ratón o contacto con touch/pen
2Botón derecho de ratón o "barrel" del pen
4Botón medio del ratón
8Botón X1 (atrás) del ratón
16Botón X2 (adelante) del ratón
32Botó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:

PropiedadDescripción
clientX / clientYPosición del apuntador en la región visible del navegador.
x / yEs un alias de las anteriores, clientX y clientY.
movementX / movementYDistancia recorrida por el apuntador desde el último evento.
offsetX / offsetYPosición del apuntador dentro del elemento target.
pageX / pageYPosición del apuntador en relación al documento.
screenX / screenYPosición del apuntado en relación a la pantalla del dispositivo.
tiltX / tiltYInclinació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:

EventoDescripción
pointerdownSe ha pulsado un botón del dispositivo apuntador en un elemento.
pointerupSe ha soltado un botón del dispositivo apuntador en un elemento.
pointermoveSe ha movido un dispositivo apuntador sobre un elemento.
pointeroverIgual al anterior, pero se propaga también a elementos secundarios.
pointerenterEl dispositivo apuntador ha entrado en un elemento.
pointerleaveEl dispositivo apuntador ha salido de un elemento.
pointeroutIgual al anterior, pero se propaga también a elementos secundarios.
pointerrawupdateSi necesitas alto nivel de refinado de eventos, puedes usar este evento.
pointercancelSe 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:

EventoDescripción
gotpointercaptureSe activa cuando se captura un puntero con .setPointerCapture().
lostpointercaptureSe 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