[ Web Proxy ]
URL:
Viewing: https://da.javascript.info/pointer-events [Back]  [Original]

Pointer events
DA

Vi nsker at gre dette open source-projekt tilgngeligt for folk over hele verden.

Hjlp med at overstte indholdet af denne tutorial til dit sprog!

    Sg p Javascript.info:
    Sg i tutorialen:
    Lyst temaMrkt tema
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek
    Dette materiale er kun tilgngeligt p flgende sprog: , English, Espaol, Franais, Indonesia, Italiano, , , , Ozbek, . Hjlp os med at overstte til Dansk.

    Pointer events

    Pointer events are a modern way to handle input from a variety of pointing devices, such as a mouse, a pen/stylus, a touchscreen, and so on.

    The brief history

    Lets make a small overview, so that you understand the general picture and the place of Pointer Events among other event types.

    • Long ago, in the past, there were only mouse events.

      Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates mousedown. So touch devices worked well with web pages.

      But touch devices have more capabilities than a mouse. For example, its possible to touch multiple points at once (multi-touch). Although, mouse events dont have necessary properties to handle such multi-touches.

    • So touch events were introduced, such as touchstart, touchend, touchmove, that have touch-specific properties (we dont cover them in detail here, because pointer events are even better).

      Still, it wasnt enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome.

    • To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices.

    As of now, Pointer Events Level 2 specification is supported in all major browsers, while the newer Pointer Events Level 3 is in the works and is mostly compatible with Pointer Events level 2.

    Unless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, theres no point in using mouse or touch events any more we can switch to pointer events.

    Then your code will work well with both touch and mouse devices.

    That said, there are some important peculiarities that one should know in order to use Pointer Events correctly and avoid surprises. Well make note of them in this article.

    Pointer event types

    Pointer events are named similarly to mouse events:

    Pointer event Similar mouse event
    pointerdown mousedown
    pointerup mouseup
    pointermove mousemove
    pointerover mouseover
    pointerout mouseout
    pointerenter mouseenter
    pointerleave mouseleave
    pointercancel -
    gotpointercapture -
    lostpointercapture -

    As we can see, for every mouse<event>, theres a pointer<event> that plays a similar role. Also there are 3 additional pointer events that dont have a corresponding mouse... counterpart, well explain them soon.

    Replacing mouse<event> with pointer<event> in our code

    We can replace mouse<event> events with pointer<event> in our code and expect things to continue working fine with mouse.

    The support for touch devices will also magically improve. Although, we may need to add touch-action: none in some places in CSS. Well cover it below in the section about pointercancel.

    Pointer event properties

    Pointer events have the same properties as mouse events, such as clientX/Y, target, etc., plus some others:

    • pointerId the unique identifier of the pointer causing the event.

      Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow).

    • pointerType the pointing device type. Must be a string, one of: mouse, pen or touch.

      We can use this property to react differently on various pointer types.

    • isPrimary is true for the primary pointer (the first finger in multi-touch).

    Some pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that:

    • width the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, its always 1.
    • height the height of the area where the pointer touches the device. Where unsupported, its always 1.
    • pressure the pressure of the pointer tip, in range from 0 to 1. For devices that dont support pressure must be either 0.5 (pressed) or 0.
    • tangentialPressure the normalized tangential pressure.
    • tiltX, tiltY, twist pen-specific properties that describe how the pen is positioned relative to the surface.

    These properties arent supported by most devices, so they are rarely used. You can find the details about them in the specification if needed.

    Multi-touch

    One of the things that mouse events totally dont support is multi-touch: a user can touch in several places at once on their phone or tablet, or perform special gestures.

    Pointer Events allow handling multi-touch with the help of the pointerId and isPrimary properties.

    Heres what happens when a user touches a touchscreen in one place, then puts another finger somewhere else on it:

    1. At the first finger touch:
      • pointerdown with isPrimary=true and some pointerId.
    2. For the second finger and more fingers (assuming the first one is still touching):
      • pointerdown with isPrimary=false and a different pointerId for every finger.

    Please note: the pointerId is assigned not to the whole device, but for each touching finger. If we use 5 fingers to simultaneously touch the screen, we have 5 pointerdown events, each with their respective coordinates and a different pointerId.

    The events associated with the first finger always have isPrimary=true.

    We can track multiple touching fingers using their pointerId. When the user moves and then removes a finger, we get pointermove and pointerup events with the same pointerId as we had in pointerdown.

    Heres the demo that logs pointerdown and pointerup events:

    Please note: you must be using a touchscreen device, such as a phone or a tablet, to actually see the difference in pointerId/isPrimary. For single-touch devices, such as a mouse, therell be always same pointerId with isPrimary=true, for all pointer events.

    Event: pointercancel

    The pointercancel event fires when theres an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated.

    Such causes are:

    • The pointer device hardware was physically disabled.
    • The device orientation changed (tablet rotated).
    • The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else.

    Well demonstrate pointercancel on a practical example to see how it affects us.

    Lets say were implementing dragndrop for a ball, just as in the beginning of the article Drag'n'Drop with mouse events.

    Here is the flow of user actions and the corresponding events:

    1. The user presses on an image, to start dragging
      • pointerdown event fires
    2. Then they start moving the pointer (thus dragging the image)
      • pointermove fires, maybe several times
    3. And then the surprise happens! The browser has native dragndrop support for images, that kicks in and takes over the dragndrop process, thus generating pointercancel event.
      • The browser now handles dragndrop of the image on its own. The user may even drag the ball image out of the browser, into their Mail program or a File Manager.
      • No more pointermove events for us.

    So the issue is that the browser hijacks the interaction: pointercancel fires in the beginning of the drag-and-drop process, and no more pointermove events are generated.

    Heres the dragndrop demo with logging of pointer events (only up/down, move and cancel) in the textarea:

    Wed like to implement the dragndrop on our own, so lets tell the browser not to take it over.

    Prevent the default browser action to avoid pointercancel.

    We need to do two things:

    1. Prevent native dragndrop from happening:
      • We can do this by setting ball.ondragstart = () => false, just as described in the article Drag'n'Drop with mouse events.
      • That works well for mouse events.
    2. For touch devices, there are other touch-related browser actions (besides dragndrop). To avoid problems with them too:
      • Prevent them by setting #ball { touch-action: none } in CSS.
      • Then our code will start working on touch devices.

    After we do that, the events will work as intended, the browser wont hijack the process and doesnt emit pointercancel.

    This demo adds these lines:

    As you can see, theres no pointercancel any more.

    Now we can add the code to actually move the ball, and our dragndrop will work for mouse devices and touch devices.

    Pointer capturing

    Pointer capturing is a special feature of pointer events.

    The idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type.

    The main method is:

    • elem.setPointerCapture(pointerId) binds events with the given pointerId to elem. After the call all pointer events with the same pointerId will have elem as the target (as if happened on elem), no matter where in document they really happened.

    In other words, elem.setPointerCapture(pointerId) retargets all subsequent events with the given pointerId to elem.

    The binding is removed:

    • automatically when pointerup or pointercancel events occur,
    • automatically when elem is removed from the document,
    • when elem.releasePointerCapture(pointerId) is called.

    Now what is it good for? Its time to see a real-life example.

    Pointer capturing can be used to simplify dragndrop kind of interactions.

    Lets recall how one can implement a custom slider, described in the Drag'n'Drop with mouse events.

    We can make a slider element to represent the strip and the runner (thumb) inside it:

    <div class="slider">
      <div class="thumb"></div>
    </div>

    With styles, it looks like this:

    And heres the working logic, as it was described, after replacing mouse events with similar pointer events:

    1. The user presses on the slider thumb pointerdown triggers.
    2. Then they move the pointer pointermove triggers, and our code moves the thumb element along.
      • As the pointer moves, it may leave the slider thumb element, go above or below it. The thumb should move strictly horizontally, remaining aligned with the pointer.

    In the mouse event based solution, to track all pointer movements, including when it goes above/below the thumb, we had to assign mousemove event handler on the whole document.

    Thats not a cleanest solution, though. One of the problems is that when a user moves the pointer around the document, it may trigger event handlers (such as mouseover) on some other elements, invoke totally unrelated UI functionality, and we dont want that.

    This is the place where setPointerCapture comes into play.

    • We can call thumb.setPointerCapture(event.pointerId) in pointerdown handler,
    • Then future pointer events until pointerup/cancel will be retargeted to thumb.
    • When pointerup happens (dragging complete), the binding is removed automatically, we dont need to care about it.

    So, even if the user moves the pointer around the whole document, events handlers will be called on thumb. Nevertheless, coordinate properties of the event objects, such as clientX/clientY will still be correct the capturing only affects target/currentTarget.

    Heres the essential code:

    thumb.onpointerdown = function(event) {
      // retarget all pointer events (until pointerup) to thumb
      thumb.setPointerCapture(event.pointerId);
    
      // start tracking pointer moves
      thumb.onpointermove = function(event) {
        // moving the slider: listen on the thumb, as all pointer events are retargeted to it
        let newLeft = event.clientX - slider.getBoundingClientRect().left;
        thumb.style.left = newLeft + 'px';
      };
    
      // on pointer up finish tracking pointer moves
      thumb.onpointerup = function(event) {
        thumb.onpointermove = null;
        thumb.onpointerup = null;
        // ...also process the "drag end" if needed
      };
    };
    
    // note: no need to call thumb.releasePointerCapture,
    // it happens on pointerup automatically

    The full demo:

    In the demo, theres also an additional element with onmouseover handler showing the current date.

    Please note: while youre dragging the thumb, you may hover over this element, and its handler does not trigger.

    So the dragging is now free of side effects, thanks to setPointerCapture.

    At the end, pointer capturing gives us two benefits:

    1. The code becomes cleaner as we dont need to add/remove handlers on the whole document any more. The binding is released automatically.
    2. If there are other pointer event handlers in the document, they wont be accidentally triggered by the pointer while the user is dragging the slider.

    Pointer capturing events

    Theres one more thing to mention here, for the sake of completeness.

    There are two events associated with pointer capturing:

    • gotpointercapture fires when an element uses setPointerCapture to enable capturing.
    • lostpointercapture fires when the capture is released: either explicitly with releasePointerCapture call, or automatically on pointerup/pointercancel.

    Summary

    Pointer events allow handling mouse, touch and pen events simultaneously, with a single piece of code.

    Pointer events extend mouse events. We can replace mouse with pointer in event names and expect our code to continue working for mouse, with better support for other device types.

    For dragndrops and complex touch interactions that the browser may decide to hijack and handle on its own remember to cancel the default action on events and set touch-action: none in CSS for elements that we engage.

    Additional abilities of pointer events are:

    • Multi-touch support using pointerId and isPrimary.
    • Device-specific properties, such as pressure, width/height, and others.
    • Pointer capturing: we can retarget all pointer events to a specific element until pointerup/pointercancel.

    As of now, pointer events are supported in all major browsers, so we can safely switch to them, especially if IE10- and Safari 12- are not needed. And even with those browsers, there are polyfills that enable the support of pointer events.

    Tutorial-oversigt

    Kommentarer

    ls dette fr du kommenterer
    • Hvis du har forslag til forbedringer - s opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
    • Hvis du ikke forstr noget i artiklen - s uddyb venligst.
    • For at indstte f ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page