In passato, indicare qualcosa sul web era semplice. Avevi un mouse, lo spostavi, a volte premevi i pulsanti e basta. Tutto ciò che non era un mouse veniva emulato come tale e gli sviluppatori sapevano esattamente cosa aspettarsi.
Tuttavia, semplice non significa necessariamente buono. Nel tempo, è diventato sempre più importante che non tutto fosse (o pretendesse di essere) un mouse: potevi avere penne sensibili alla pressione e all'inclinazione, per una straordinaria libertà creativa; potevi usare le dita, quindi tutto ciò di cui avevi bisogno era il dispositivo e la tua mano; e, a proposito, perché non usare più di un dito?
Da un po' di tempo sono disponibili gli eventi tocco per aiutarci a risolvere il problema, ma si tratta di un'API completamente separata specifica per il tocco, che ti costringe a codificare due modelli di eventi distinti se vuoi supportare sia il mouse sia il tocco. Chrome 55 è dotato di uno standard più recente che unifica entrambi i modelli: gli eventi del cursore.
Un modello di singolo evento
Gli eventi del cursore unificano il modello di input del cursore per il browser, combinando tocchi, penne e mouse in un unico insieme di eventi. Ad esempio:
document.addEventListener('pointermove',
ev => console.log('The pointer moved.'));
foo.addEventListener('pointerover',
ev => console.log('The pointer is now over foo.'));
Ecco un elenco di tutti gli eventi disponibili, che dovrebbero essere abbastanza familiari se hai dimestichezza con gli eventi del mouse:
pointerover
|
Il cursore è entrato nell'area delimitata dell'elemento.
Questo accade immediatamente per i dispositivi che supportano il passaggio del mouse o prima di un
pointerdown evento per i dispositivi che non lo supportano.
|
pointerenter
|
Simile a pointerover , ma non genera una gerarchia e gestisce i discendenti in modo diverso.
Dettagli sulle specifiche.
|
pointerdown
|
Il cursore è entrato nello stato del pulsante attivo, con un pulsante premuto o il contatto stabilito, a seconda della semantica del dispositivo di input. |
pointermove
|
Il cursore ha cambiato posizione. |
pointerup
|
Il cursore ha lasciato lo stato del pulsante attivo. |
pointercancel
|
È successo qualcosa che fa pensare che il cursore non emetterà altri eventi. Ciò significa che devi annullare eventuali azioni in corso e tornare a uno stato di input neutro. |
pointerout
|
Il cursore ha lasciato il riquadro di delimitazione dell'elemento o della schermata. Anche dopo un
pointerup , se il dispositivo non supporta il passaggio del mouse.
|
pointerleave
|
Simile a pointerout , ma non genera una gerarchia e gestisce i discendenti in modo diverso.
Dettagli sulle specifiche.
|
gotpointercapture
|
L'elemento ha ricevuto il controllo del cursore. |
lostpointercapture
|
Il cursore che stava per essere acquisito è stato rilasciato. |
Diversi tipi di input
In genere, gli eventi relativi al cursore ti consentono di scrivere codice in modo indipendente dall'input, senza dover registrare gestori di eventi separati per diversi dispositivi di input.
Naturalmente, dovrai comunque tenere conto delle differenze tra i tipi di input, ad esempio se si applica il concetto di passaggio del mouse. Se vuoi distinguere i diversi tipi di dispositivi di input, ad esempio per fornire codice/funzionalità separati per input diversi, puoi farlo all'interno degli stessi gestori eventi utilizzando la proprietà pointerType
dell'interfaccia PointerEvent
. Ad esempio, se stai codificando un riquadro di navigazione laterale, potresti avere la seguente logica per l'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;
}
Azioni predefinite
Nei browser con tocco, vengono utilizzati determinati gesti per scorrere, aumentare lo zoom o aggiornare la pagina.
Nel caso degli eventi tocco, riceverai comunque eventi durante l'esecuzione di queste azioni predefinite. Ad esempio, touchmove
verrà comunque attivato mentre l'utente scorre.
Con gli eventi del cursore, ogni volta che viene attivata un'azione predefinita come lo scorrimento o lo zoom, viene visualizzato un evento pointercancel
per informarti che il browser ha assunto il controllo del cursore. Ad esempio:
document.addEventListener('pointercancel',
ev => console.log('Go home, the browser is in charge now.'));
Velocità integrata: questo modello consente un rendimento migliore per impostazione predefinita rispetto agli eventi touch, per i quali è necessario utilizzare ascoltatori di eventi passivi per ottenere lo stesso livello di reattività.
Puoi impedire al browser di assumere il controllo con la proprietà CSS
touch-action
. Se lo imposti su none
su un elemento, verranno disattivate tutte le azioni predefinite dal browser avviate su quell'elemento. Esistono però diversi altri valori per un controllo più granulare, ad esempio pan-x
, per consentire al browser di reagire al movimento sull'asse x, ma non sull'asse y. Chrome 55 supporta i seguenti valori:
auto
|
Valore predefinito: il browser può eseguire qualsiasi azione predefinita. |
none
|
Al browser non è consentito eseguire azioni predefinite. |
pan-x
|
Il browser può eseguire solo l'azione predefinita di scorrimento orizzontale. |
pan-y
|
Il browser è autorizzato a eseguire solo l'azione predefinita di scorrimento verticale. |
pan-left
|
Il browser è autorizzato a eseguire solo l'azione predefinita di scorrimento orizzontale e solo a eseguire la panoramica della pagina verso sinistra. |
pan-right
|
Il browser è autorizzato a eseguire solo l'azione predefinita di scorrimento orizzontale e solo a eseguire la panoramica della pagina verso destra. |
pan-up
|
Il browser è autorizzato a eseguire solo l'azione predefinita di scorrimento verticale e solo a eseguire la panoramica della pagina verso l'alto. |
pan-down
|
Il browser è autorizzato a eseguire solo l'azione predefinita di scorrimento verticale e solo a eseguire la panoramica della pagina verso il basso. |
manipulation
|
Il browser può eseguire solo azioni di scorrimento e zoom. |
Acquisizione del cursore
Hai mai trascorso un'ora frustrante a eseguire il debug di un evento mouseup
non funzionante, fino a quando non hai capito che il problema è che l'utente rilascia il pulsante
al di fuori del target di clic? No? Ok, forse è solo un mio problema.
Tuttavia, finora non esisteva un modo davvero efficace per risolvere questo problema. Certo,
puoi configurare l'handler mouseup
nel documento e salvare un po' di stato nella
tua applicazione per tenere traccia delle cose. Tuttavia, non è la soluzione più pulita, soprattutto se stai creando un componente web e stai cercando di mantenere tutto ben isolato.
Con gli eventi del cursore è disponibile una soluzione molto migliore: puoi acquisire il cursore,
in modo da ricevere l'evento pointerup
(o qualsiasi altro dei suoi amici sfuggenti).
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!'));
Supporto browser
Al momento della stesura di questo articolo, gli eventi relativi al cursore sono supportati in Internet Explorer 11, Microsoft Edge, Chrome e Opera e parzialmente in Firefox. Puoi trovare un elenco aggiornato su caniuse.com.
Puoi utilizzare il polyfill Pointer Events per colmare le lacune. In alternativa, il controllo del supporto del browser in fase di esecuzione è semplice:
if (window.PointerEvent) {
// Yay, we can use pointer events!
} else {
// Back to mouse and touch events, I guess.
}
Gli eventi del cursore sono candidati ideali per il miglioramento progressivo: basta modificare i metodi di inizializzazione per eseguire il controllo riportato sopra, aggiungere gestori di eventi del cursore nel blocco if
e spostare i gestori di eventi del mouse/touch nel blocco else
.
Provali e facci sapere cosa ne pensi.