1

If there are three divs div1,div2,div3 side by side. If I do mousedown on div1 and then if I move mouse onto div2 then div2 mousemove will be fired with the target as div2. But in mobile if do same even mousedown(touchstart) on div1 and move the mouse onto div2 then mousemove(touchmove) is firing with target as div1 itself. I need mobile touchmove event target as div2?

Why there is a difference in behavior and what we can do?

Below is the sample I have did to explain my issue,

    
            var testString = '';
    
            handleMouseMoveListener = (e) => {
                //console.log(e.target.id);
                e.preventDefault();
                e.stopPropagation();
                this.testString = this.testString + ' ' + e.target.id;
            }
    
            handleMouseUpHandler = (e) => {
    
                alert(this.testString);
                this.testString = '';
            }
    
    
            let elementsArray = document.querySelectorAll("div");
            elementsArray.forEach(function (elem) {
                elem.addEventListener('mousemove', this.handleMouseMoveListener);
                elem.addEventListener('touchmove', this.handleMouseMoveListener);
                elem.addEventListener('mouseup', this.handleMouseUpHandler);
                elem.addEventListener('touchend', this.handleMouseUpHandler);
            });
   <!DOCTYPE html>
    <html>
    <head>
        <title>Page Title</title>
    
        <style>
            div {
                display: inline-block;
                width: 150px;
                height: 150px;
                color: red;
                border: 1px solid black;
            }
        </style>
    </head>
    <body>
    
    
        <div id='div1'>div1</div>
        <div id='div2'>div2</div>
        <div id='div3'>div3</div>
    

    
    </body>
    </html>
Ericgit
  • 6,089
  • 2
  • 42
  • 53
Mahesh
  • 823
  • 1
  • 11
  • 29

1 Answers1

4

This is the specs defined behavior:

The target of this event must be the same Element on which the touch point started when it was first placed on the surface, even if the touch point has since moved outside the interactive area of the target element.

As to why it's defined this way... I'm not too sure, but I think it goes back to when iphone were alone in the market, and specs just embraced their behavior as counter-intuitive to us web-devs it may be.

For the what can we do, it has already been asked and answered before: you can use document.elementFromPoint() from the clientX and clientY values your Touch instance will expose:

if( document.ontouchmove === undefined ) {
  console.log( "please enable your dev-tools's Responsive mode" );
}

document.querySelectorAll( '.container div' ).forEach( (elem) => {
  elem.addEventListener( 'touchstart', prevent );
  elem.addEventListener( 'touchmove', handleTouchMove );
} );

function handleTouchMove( evt ) {
  //prevent( evt );
  deactivateTarget(); // clean
  
  evt.target.classList.add( 'target' ); // make the official target's text red

  const touch = evt.changedTouches[ 0 ];
  const actualTarget = document.elementFromPoint( touch.clientX, touch.clientY );
  if( actualTarget ) {
    actualTarget.classList.add( 'active' ); // make the hovered element green
  }
}
function deactivateTarget() {
  document.querySelectorAll( '.active,.target' ).forEach( (elem) => {
    elem.classList.remove( 'active', 'target' );
  })  
}

function prevent( evt ) {
  evt.preventDefault();
}
.container div {
  display: inline-block;
  width: 150px;
  height: 50px;
  border: 1px solid black;
}
.container div.active {
  background: green;
}
.container div.target {
  color: red;
}
<div class="container">
  <div>div1</div>
  <div>div2</div>
  <div>div3</div>
  <div>div4</div>
  <div>div5</div>
  <div>div6</div>
  <div>div7</div>
  <div>div8</div>
  <div>div9</div>
  <div>div10</div>
  <div>div11</div>
  <div>div12</div>
  <div>div13</div>
  <div>div14</div>
  <div>div15</div>
</div>
Kaiido
  • 123,334
  • 13
  • 219
  • 285