Wijst de weg vooruit

Sérgio Gomes

Vroeger was het eenvoudig om naar dingen op internet te wijzen. Je had een muis, je bewoog hem, soms drukte je op knoppen, en dat was het. Alles wat geen muis was, werd als één muis nagebootst, en ontwikkelaars wisten precies waar ze op moesten rekenen.

Eenvoudig betekent echter niet noodzakelijkerwijs goed. In de loop van de tijd werd het steeds belangrijker dat niet alles een muis was (of deed alsof het zo was): je kon drukgevoelige en kantelgevoelige pennen hebben, voor een verbazingwekkende creatieve vrijheid; je kon je vingers gebruiken, dus alles wat je nodig had was het apparaat en je hand; en hey, waarom gebruik je niet meer dan één vinger terwijl je toch bezig bent?

We hebben al een tijdje aanraakgebeurtenissen gehad om ons daarbij te helpen, maar het is een geheel aparte API specifiek voor aanraking, waardoor je twee afzonderlijke gebeurtenismodellen moet coderen als je zowel muis als aanraking wilt ondersteunen. Chrome 55 wordt geleverd met een nieuwere standaard die beide modellen verenigt: pointer-gebeurtenissen.

Eén gebeurtenismodel

Aanwijzergebeurtenissen verenigen het aanwijzerinvoermodel voor de browser, waardoor aanraking, pennen en muizen samenkomen in één reeks gebeurtenissen. Bijvoorbeeld:

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

Hier is een lijst met alle beschikbare gebeurtenissen, die er redelijk bekend uit zouden moeten zien als u bekend bent met muisgebeurtenissen:

pointerover De aanwijzer is in het selectiekader van het element terechtgekomen. Dit gebeurt onmiddellijk voor apparaten die hover ondersteunen, of vóór een pointerdown gebeurtenis voor apparaten die dat niet doen.
pointerenter Vergelijkbaar met pointerover , maar bubbelt niet en behandelt nakomelingen anders. Details over de specificatie .
pointerdown De aanwijzer is in de actieve knopstatus terechtgekomen, waarbij een knop is ingedrukt of er contact is gemaakt, afhankelijk van de semantiek van het invoerapparaat.
pointermove De wijzer is van positie veranderd.
pointerup De aanwijzer heeft de actieve knopstatus verlaten.
pointercancel Er is iets gebeurd waardoor het onwaarschijnlijk is dat de aanwijzer nog meer gebeurtenissen zal uitzenden. Dit betekent dat u alle lopende acties moet annuleren en terug moet gaan naar een neutrale invoerstatus.
pointerout De aanwijzer heeft het selectiekader van het element of scherm verlaten. Ook na een pointerup , als het apparaat hover niet ondersteunt.
pointerleave Vergelijkbaar met pointerout , maar bubbelt niet en behandelt nakomelingen anders. Details over de specificatie .
gotpointercapture Element heeft aanwijzeropname ontvangen.
lostpointercapture De wijzer die werd vastgelegd, is vrijgegeven.

Verschillende invoertypen

Over het algemeen kunt u met Pointer Events code schrijven op een invoeronafhankelijke manier, zonder dat u afzonderlijke gebeurtenishandlers voor verschillende invoerapparaten hoeft te registreren. Natuurlijk moet je nog steeds rekening houden met de verschillen tussen invoertypen, bijvoorbeeld of het concept van zweven van toepassing is. Als u verschillende typen invoerapparaten van elkaar wilt onderscheiden – misschien om afzonderlijke code/functionaliteit voor verschillende invoer te bieden – kunt u dit echter doen vanuit dezelfde gebeurtenishandlers met behulp van de pointerType eigenschap van de PointerEvent interface. Als u bijvoorbeeld een zijnavigatielade codeert, kunt u de volgende logica gebruiken voor uw pointermove gebeurtenis:

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;
}

Standaardacties

In browsers met aanraakbediening worden bepaalde gebaren gebruikt om de pagina te laten scrollen, zoomen of vernieuwen. In het geval van aanraakgebeurtenissen ontvangt u nog steeds gebeurtenissen terwijl deze standaardacties plaatsvinden. touchmove wordt bijvoorbeeld nog steeds geactiveerd terwijl de gebruiker aan het scrollen is.

Bij pointergebeurtenissen krijgt u telkens wanneer een standaardactie zoals scrollen of zoomen wordt geactiveerd, een pointercancel gebeurtenis, om u te laten weten dat de browser de controle over de aanwijzer heeft overgenomen. Bijvoorbeeld:

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

Ingebouwde snelheid : dit model zorgt standaard voor betere prestaties, vergeleken met aanraakgebeurtenissen, waarbij u passieve gebeurtenislisteners zou moeten gebruiken om hetzelfde reactieniveau te bereiken.

U kunt voorkomen dat de browser de controle overneemt met de CSS-eigenschap touch-action . Als u dit voor een element op none zet, worden alle door de browser gedefinieerde acties uitgeschakeld die via dat element zijn gestart. Maar er zijn een aantal andere waarden voor een fijnmazigere controle, zoals pan-x , waarmee de browser kan reageren op bewegingen op de x-as, maar niet op de y-as. Chrome 55 ondersteunt de volgende waarden:

auto Standaard; de browser kan elke standaardactie uitvoeren.
none De browser mag geen standaardacties uitvoeren.
pan-x De browser mag alleen de standaardactie horizontaal scrollen uitvoeren.
pan-y De browser mag alleen de standaardactie verticaal scrollen uitvoeren.
pan-left De browser mag alleen de standaardactie horizontaal scrollen uitvoeren en de pagina alleen naar links pannen.
pan-right De browser mag alleen de standaardactie horizontaal scrollen uitvoeren en de pagina alleen naar rechts pannen.
pan-up De browser mag alleen de standaardactie verticaal scrollen uitvoeren en alleen de pagina naar boven pannen.
pan-down De browser mag alleen de standaardactie verticaal scrollen uitvoeren en alleen de pagina naar beneden pannen.
manipulation De browser mag alleen scroll- en zoomacties uitvoeren.

Aanwijzer vastleggen

Heeft u ooit een frustrerend uur besteed aan het debuggen van een defecte mouseup gebeurtenis, totdat u zich realiseerde dat dit komt doordat de gebruiker de knop loslaat buiten uw klikdoel? Nee? Oké, misschien ligt het dan aan mij.

Toch was er tot nu toe geen echt goede manier om dit probleem aan te pakken. Natuurlijk kunt u de mouseup handler voor het document instellen en een status in uw toepassing opslaan om de zaken bij te houden. Dat is echter niet de schoonste oplossing, vooral als je een webcomponent bouwt en alles mooi en geïsoleerd probeert te houden.

Met pointer-gebeurtenissen is er een veel betere oplossing: je kunt de pointer vastleggen, zodat je zeker weet dat je die pointerup gebeurtenis (of een van zijn andere ongrijpbare vrienden) krijgt.

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!'));

Browser-ondersteuning

Op het moment van schrijven worden Pointer Events ondersteund in Internet Explorer 11, Microsoft Edge, Chrome en Opera, en gedeeltelijk ondersteund in Firefox. Een actuele lijst vindt u op caniuse.com .

U kunt de polyfill Pointer Events gebruiken om de gaten op te vullen. Als alternatief is het controleren op browserondersteuning tijdens runtime eenvoudig:

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

Pointer-gebeurtenissen zijn een fantastische kandidaat voor progressieve verbetering: pas gewoon uw initialisatiemethoden aan om de bovenstaande controle uit te voeren, voeg pointer-gebeurtenishandlers toe in het if blok en verplaats uw muis/aanraakgebeurtenishandlers naar het else blok.

Dus ga je gang, geef ze een draai en laat ons weten wat je ervan vindt!