删除"触摸开始"目标后,触摸移动事件不会触发

Ale*_*tau 6 javascript events google-chrome touch pointer-events

我正在尝试使用下一个模式实现类似拖动的功能:

  • 订阅marker指针向下事件.
  • 当Down事件触发时,订阅Window Pointer Move和Up事件并删除标记.
  • 移动时执行一些操作.
  • 当Up事件触发取消订阅Move和Up时.

这适用于鼠标事件,但不适用于Touch事件.删除Touch Start目标元素后,它们不会触发.我尝试使用指针事件Polyfill,但它也不起作用.

我正在使用Chrome Dev Tools来模拟触摸事件.看样品:

initTestBlock('mouse', {
  start: 'mousedown',
  move: 'mousemove',
  end: 'mouseup'
});
initTestBlock('touch', {
  start: 'touchstart',
  move: 'touchmove',
  end: 'touchend'
});
initTestBlock('touch-no-remove', {
  start: 'touchstart',
  move: 'touchmove',
  end: 'touchend'
}, true);

function initTestBlock(id, events, noRemove) {
  var block = document.getElementById(id);
  var parent = block.querySelector('.parent');
  var target = block.querySelector('.target');
  target.addEventListener(events.start, function(e) {
    console.log(e.type);
    if (!noRemove) {
      setTimeout(function() {
        // Remove target
        target.parentElement.removeChild(target);
      }, 1000);
    }

    function onMove(e) {
      console.log(e.type);
      var pt = getCoords(e);
      parent.style.left = pt.x + 'px';
      parent.style.top = pt.y + 'px';
    }

    function onEnd(e) {
      console.log(e.type);
      window.removeEventListener(events.move, onMove);
      window.removeEventListener(events.end, onEnd);
    }

    window.addEventListener(events.move, onMove);
    window.addEventListener(events.end, onEnd);

  });
}

// Returns pointer coordinates
function getCoords(e) {
  if (e instanceof TouchEvent) {
    return {
      x: e.touches[0].pageX,
      y: e.touches[0].pageY
    };
  }
  return {
    x: e.pageX,
    y: e.pageY
  };
}

window.addEventListener('selectstart', function() {
  return false;
}, true);
Run Code Online (Sandbox Code Playgroud)
.parent {
  background: darkred;
  color: white;
  width: 10em;
  height: 10em;
  position: absolute;
}
.target {
  background: orange;
  width: 4em;
  height: 4em;
}
#mouse .parent {
  left: 0em;
}
#touch .parent {
  left: 11em;
}
#touch-no-remove .parent {
  left: 22em;
}
Run Code Online (Sandbox Code Playgroud)
<div id="mouse">
  <div class="parent">Mouse events
    <div class="target">Drag here</div>
  </div>
</div>
<div id="touch">
  <div class="parent">Touch events
    <div class="target">Drag here</div>
  </div>
</div>
<div id="touch-no-remove">
  <div class="parent">Touch (no remove)
    <div class="target">Drag here</div>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)

Zit*_*tRo 8

的确,根据文档,

如果从文档中删除目标元素,则事件仍将以其为目标,因此不一定会冒泡到窗口或文档.如果在触摸元素时有任何元素被删除的风险,最佳做法是将触摸侦听器直接附加到目标.

事实证明,解决方案是将附件touchmovetouchend侦听器连接到event.target自身,例如:

element.addEventListener("touchstart", (event) => {
    const onTouchMove = () => {
        // handle touchmove here
    }
    const onTouchEnd = () => {
        event.target.removeEventListener("touchmove", onTouchMove);
        event.target.removeEventListener("touchend", onTouchEnd);
        // handle touchend here
    }
    event.target.addEventListener("touchmove", onTouchMove);
    event.target.addEventListener("touchend", onTouchEnd);
    // handle touchstart here
});
Run Code Online (Sandbox Code Playgroud)

即使event.target从DOM中删除元素,事件也会继续正常触发并提供正确的坐标.

  • 为什么这有效?`event.target` 位于 DOM 中的元素内部,因此如果该元素从 DOM 中删除,目标也会被删除。如果目标也从 DOM 中删除,目标上的事件如何冒泡? (2认同)
  • 这怎么没有被标记为答案,这救了我的命!非常感谢 。 (2认同)

Ale*_*tau 5

诀窍是隐藏元素直到触摸移动完成,而不是删除它。这是一些示例(在 Chrome Dev Tools 中启用触摸模式并选择一些设备或使用真实设备):https : //jsfiddle.net/alexanderby/na3rumjg/

var marker = document.querySelector('circle');
var onStart = function(startEvt) {
  startEvt.preventDefault(); // Prevent scroll
  marker.style.visibility = 'hidden'; // Hide target element
  var rect = document.querySelector('rect');
  var initial = {
    x: +rect.getAttribute('x'),
    y: +rect.getAttribute('y')
  };
  var onMove = function(moveEvt) {
    rect.setAttribute('x', initial.x + moveEvt.touches[0].clientX - startEvt.touches[0].clientX);
    rect.setAttribute('y', initial.y + moveEvt.touches[0].clientY - startEvt.touches[0].clientY);
  };
  var onEnd = function(endEvt) {
    window.removeEventListener('touchmove', onMove);
    window.removeEventListener('touchend', onEnd);
    marker.removeEventListener('touchstart', onStart);
    marker.parentElement.removeChild(marker); // Remove target element
  };
  window.addEventListener('touchmove', onMove);
  window.addEventListener('touchend', onEnd);
};
marker.addEventListener('touchstart', onStart);
Run Code Online (Sandbox Code Playgroud)
<svg>
  <circle r="20" cx="50" cy="20" cursor="move"/>
  <rect x="10" y="50" width="80" height="80" />
</svg>
Run Code Online (Sandbox Code Playgroud)