Search code examples
javascriptandroiddomsmartphonepointer-events

PointerEvents: Detect touching "through" an element


Using pointer events, I can't find the right event to trigger for finger-based touches on smartphones (tested with Chrome Android and Chrome Devtools with mobile emulation).

What I need: A "hover" event if you touch action passes through an element while holding the finger down moving over the screen.

That is, put your finger down outside the element, move through it, and move finger up only after completely passing through the element.

I attached a code snipped to clearify: I don't need events for the blue elements, I would only need respective "in/out" events for the red element in the snippet. The sample JS code will fire for the mouse, but on mobile it does not trigger any console.infos.

var elem = document.querySelector(".element");

elem.addEventListener("pointerover", function() {
    console.clear();
    console.info("pointerover triggered");
});
elem.addEventListener("pointerenter", function() {
    console.clear();
    console.info("pointerenter triggered");
});
elem.addEventListener("pointerleave", function() {
    console.clear();
    console.info("pointerleave triggered");
});
.outer {
    width: 100px;
    height: 100px;
    border: 3px solid grey;
    font-size: 12px;
    color: white;
    text-align:center;
    touch-action: none;
    
}

.start {
   position: relative;
   top:0px;
   left:0px;
   width: 100px;
   height: 20px;
   background-color: blue;
}

.element {
   position: relative;
   top: 20px;
   left: 0px;
   width: 100px;
   height: 20px;
   background-color: red;
}

.end {
   position: relative;
   top: 40px;
   right: 0;
   width: 100px;
   height: 20px;
   background-color: blue;
}
<div class="outer">
    <div class="start">Start touch here</div>
    <div class="element">Move over here</div>
    <div class="end">End touch here</div>
</div>


Solution

  • I hope that I understand you correctly. I wrote and tested for you two different solutions: pointerevents and touch events. In each move event from this events you can detect the current element with the function document.elementFromPoint().

    Solution with pointerevents

    Maybe you can use pointerevents – they work in Chrome Devtools with mobile emulation, but not work on my Android device (I think my device is too old). Or maybe you can use it with Pointer Events Polyfill. The Browser compatibility for pointerevents you can see here.

    var elementFromPoint,
        isFingerDown = false,
        isThroughElemMoved = false,
        elem = document.querySelector('.element'),
        output = document.querySelector('#output');
    
    document.addEventListener('pointerdown', function(e)
    {
        if(elem != e.target)
        {
            isFingerDown = true;
            output.innerHTML = 'pointer-START';
        }
    });
    
    document.addEventListener('pointermove', function(e)
    {
        elementFromPoint = document.elementFromPoint(e.pageX - window.pageXOffset, e.pageY - window.pageYOffset);
    
        if(elem == elementFromPoint)
        {
            isThroughElemMoved = true;
            output.innerHTML = 'pointer-MOVE';
        }
    });
    
    document.addEventListener('pointerup', function(e)
    {
        if(isFingerDown && isThroughElemMoved && elem != elementFromPoint)
            output.innerHTML = 'It is done!';
    
        isFingerDown = isThroughElemMoved = false;
    });
    .outer
    {
        width: 100px;
        height: 100px;
        border: 3px solid grey;
        font-size: 12px;
        color: white;
        text-align: center;
        /*touch-action: none*/
    }
    .outer div{position: relative; left: 0; height: 20px}
    .start{top: 0; background: blue}
    .element{top: 20px; background: red}
    .end{top: 40px; background: blue}
    <div class="outer">
        <div class="start">Start touch here</div>
        <div class="element">Move over here</div>
        <div class="end">End touch here</div>
    </div>
    <br><br>
    <div id="output">info</div>

    Solution with touch events

    But you can use touch events too. Unfortunatelly, the events touchenter and touchleave were deleted from the specification and because of them we have to write a workaround using document.elementFromPoint() too.

    The following snippet works only in the mobile emulation (tested with Chrome Devtools) or on devices which support touch events (tested with Android).

    var elementFromPoint,
        isFingerDown = false,
        isThroughElemMoved = false,
        elem = document.querySelector('.element'),
        output = document.querySelector('#output');
    
    document.addEventListener('touchstart', function(e)
    {
        if(elem != e.target)
        {
            isFingerDown = true;
            output.innerHTML = 'touch-START';
        }
    });
    
    document.addEventListener('touchmove', function(e)
    {
        var touch = e.touches[0];
        
        elementFromPoint = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset);
    
        if(elem == elementFromPoint)
        {
            isThroughElemMoved = true;
            output.innerHTML = 'touch-MOVE';
        }
    });
    
    document.addEventListener('touchend', function(e)
    {
        if(isFingerDown && isThroughElemMoved && elem != elementFromPoint)
            output.innerHTML = 'It is done!';
    
        isFingerDown = isThroughElemMoved = false;
    });
    .outer
    {
        width: 100px;
        height: 100px;
        border: 3px solid grey;
        font-size: 12px;
        color: white;
        text-align: center;
        /*touch-action: none*/
    }
    .outer div{position: relative; left: 0; height: 20px}
    .start{top: 0; background: blue}
    .element{top: 20px; background: red}
    .end{top: 40px; background: blue}
    <div class="outer">
        <div class="start">Start touch here</div>
        <div class="element">Move over here</div>
        <div class="end">End touch here</div>
    </div>
    <br><br>
    <div id="output">info</div>

    Maybe the following links can help you: