Señalar el camino a seguir

Sérgio Gomes

Apuntar a objetos en la Web solía ser simple. Tenías un mouse, lo moviste a veces presionaban botones, y eso fue todo. Todo lo que no fuera mouse se emulaba como uno, y los desarrolladores sabían exactamente con qué contar.

Sin embargo, simple no necesariamente significa bueno. Con el tiempo, se volvió cada vez más importante que no todo fuera (o fingiera ser) un mouse: podías tener bolígrafos sensibles a la presión y compatibles con la inclinación, para brindar una increíble libertad creativa podrías usarías los dedos, por lo que todo lo que necesitabas era el dispositivo y tu mano; y bueno, ¿por qué no uses más de un dedo mientras lo haces?

Tuvimos eventos táctiles. para ayudarnos con eso, pero son una API completamente independiente específicamente para el tacto, lo que te obliga a codificar dos modelos de eventos separados si admitir el mouse y el tacto. Chrome 55 se envía con un estándar más nuevo que unifica ambos modelos: eventos de puntero.

Un modelo de evento único

Los eventos de puntero unifican el modelo de entrada de puntero para el navegador, que combina la función táctil, los bolígrafos y los mouses en un solo conjunto de eventos. Por ejemplo:

document.addEventListener('pointermove',
    ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
    ev => console.log('The pointer is now over foo.'));

Esta es una lista de todos los eventos disponibles, que debería resultarte familiar si estás familiarizado con los eventos del mouse:

pointerover El puntero ingresó en el cuadro delimitador del elemento. Esto sucede inmediatamente en los dispositivos que permiten colocar el cursor sobre un elemento o antes de pointerdown para los dispositivos que no lo hacen.
pointerenter Es similar a pointerover, pero no muestra burbujas ni controladores. y sus elementos subordinados de forma diferente. Detalles sobre la especificación
pointerdown El puntero ingresó al estado del botón activo, y cualquiera de los dos presionado o que se establezca el contacto, según la semántica de la dispositivo de entrada.
pointermove El puntero cambió de posición.
pointerup El puntero salió del estado del botón activo.
pointercancel Se produjo un error, lo que significa que es poco probable que el puntero emita alguna o más eventos. Esto significa que debes cancelar cualquier acción en curso y continuar a un estado de entrada neutral.
pointerout El puntero salió del cuadro delimitador del elemento o la pantalla. También después de un pointerup, si el dispositivo no permite colocar el cursor sobre un elemento
pointerleave Es similar a pointerout, pero no muestra burbujas ni controladores. y sus elementos subordinados de forma diferente. Detalles sobre la especificación
gotpointercapture El elemento recibió captura de puntero.
lostpointercapture El puntero que se estaba capturando lanzamiento.

Diferentes tipos de entrada

En general, los eventos de puntero te permiten escribir código de manera independiente de la entrada sin tener que registrar controladores de eventos separados para los distintos dispositivos de entrada. Por supuesto, aún deberás tener en cuenta las diferencias entre los tipos de entrada, por ejemplo, se aplica el concepto de colocar el cursor sobre un elemento. Si quieres diferenciar diferentes tipos de dispositivos de entrada, quizás para proporcionar código o funcionalidad independientes para diferentes entradas; sin embargo, puedes hacerlo desde dentro de los mismos controladores de eventos con la propiedad pointerType del PointerEvent interfaz de usuario. Por ejemplo, si estuvieras programando un panel lateral de navegación, podrías tener la siguiente lógica en tu evento pointermove:

switch(ev.pointerType) {
    case 'mouse':
    // Do nothing.
    break;
    case 'touch':
    // Allow drag gesture.
    break;
    case 'pen':
    // Also allow drag gesture.
    break;
    default:
    // Getting an empty string means the browser doesn't know
    // what device type it is. Let's assume mouse and do nothing.
    break;
}

Acciones predeterminadas

En los navegadores con opciones táctiles, se usan ciertos gestos para hacer que la página se desplace, haga zoom o se actualice. En el caso de los eventos táctiles, seguirás recibiendo eventos mientras estos eventos se producen acciones. Por ejemplo, se seguirá activando touchmove mientras el usuario se desplaza.

Con los eventos del puntero, cada vez que se activa una acción predeterminada, como el desplazamiento o el zoom, recibirás un evento pointercancel que te informará que el navegador completó control del puntero. Por ejemplo:

document.addEventListener('pointercancel',
    ev => console.log('Go home, the browser is in charge now.'));

Velocidad integrada: Este modelo proporciona un mejor rendimiento de forma predeterminada. en comparación con los eventos táctiles, en los que necesitarías usar objetos de escucha de eventos pasivos para lograr el mismo nivel de capacidad de respuesta.

Puedes impedir que el navegador tome el control con la touch-action propiedad de CSS. Si se establece en none en un elemento, se inhabilitarán todos acciones definidas por el navegador que se inician sobre ese elemento. Pero hay varios otros valores para un control más detallado, como pan-x, para permitir el navegador reaccione al movimiento en el eje x, pero no en el eje y. Chrome 55 admite los siguientes valores:

auto Predeterminado; el navegador puede realizar cualquier acción predeterminada.
none El navegador no puede realizar ninguna acción predeterminada.
pan-x El navegador solo puede realizar la acción predeterminada de desplazamiento horizontal.
pan-y El navegador solo puede realizar la acción predeterminada de desplazamiento vertical.
pan-left El navegador solo puede realizar la acción predeterminada de desplazamiento horizontal y solo desplazar la página hacia la izquierda.
pan-right El navegador solo puede realizar la acción predeterminada de desplazamiento horizontal y solo desplazar la página hacia la derecha.
pan-up El navegador solo puede realizar la acción predeterminada de desplazamiento vertical y solo para desplazar la página hacia arriba.
pan-down El navegador solo puede realizar la acción predeterminada de desplazamiento vertical y solo desplazar la página hacia abajo.
manipulation El navegador solo puede realizar acciones de desplazamiento y zoom.

Captura del puntero

¿Alguna vez pasaste una hora frustrante para depurar un mouseup dañado? hasta que te diste cuenta de que es porque el usuario suelta el botón fuera de tu objetivo de clics? ¿No? Bueno, quizás solo seré yo.

Sin embargo, hasta ahora no había una forma realmente buena de abordar este problema. Claro, puedes configurar el controlador mouseup en el documento y guardar algún estado en tu aplicación para hacer un seguimiento de los aspectos. Esa no es la solución más limpia, sobre todo si desea crear un componente web y tratar de que todo esté lindo y aisladas.

Los eventos de puntero son una solución mucho mejor: puedes capturar el puntero, para asegurarte de obtener ese evento pointerup (o cualquier otro de sus eventos elusivos amigos).

const foo = document.querySelector('#foo');
foo.addEventListener('pointerdown', ev => {
    console.log('Button down, capturing!');
    // Every pointer has an ID, which you can read from the event.
    foo.setPointerCapture(ev.pointerId);
});

foo.addEventListener('pointerup', 
    ev => console.log('Button up. Every time!'));

Navegadores compatibles

Al momento de la redacción, los eventos de puntero son compatibles con Internet Explorer 11, Microsoft Edge, Chrome y Opera, y parcialmente compatibles con Firefox. Puedes Encuentra una lista actualizada en caniuse.com.

Puedes usar el polyfill de eventos de puntero para lo siguiente: para llenar los vacíos. Como alternativa, comprobar la compatibilidad del navegador en el tiempo de ejecución sencillo:

if (window.PointerEvent) {
    // Yay, we can use pointer events!
} else {
    // Back to mouse and touch events, I guess.
}

Los eventos de puntero son un gran candidato para la mejora progresiva: modifica tus métodos de inicialización para hacer la verificación anterior, agrega un evento de puntero en el bloque if y mueve los controladores de eventos de mouse/táctil al Bloque else.

Así que no dejes de probarlos y contarnos tu opinión.