Событие Touch Move не срабатывает после удаления цели Touch Start
Я пытаюсь реализовать функциональность, похожую на перетаскивание, используя следующий шаблон:
- Подписаться на маркер Pointer Down.
- Когда происходит событие "Вниз", подпишитесь на события "Перемещение окна" и "Вверх" и удалите маркер.
- Выполните некоторые действия во время перемещения.
- При возникновении события 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);
.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;
}
<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>
2 ответа
Хитрость заключается в том, чтобы скрыть элемент до тех пор, пока не закончится перемещение касанием, а не удалить его. Вот несколько примеров (включите сенсорный режим в 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);
<svg>
<circle r="20" cx="50" cy="20" cursor="move"/>
<rect x="10" y="50" width="80" height="80" />
</svg>
Действительно, согласно документам,
Если целевой элемент удаляется из документа, события по-прежнему будут нацелены на него и, следовательно, не обязательно будут всплывать в окне или документе. Если существует какой-либо риск того, что элемент будет удален во время прикосновения, лучше всего прикрепить слушателей касания непосредственно к цели.
Оказывается, решение состоит в том, чтобы прикрепить touchmove
а также touchend
слушатели 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
});
Даже если event.target
элемент удаляется из DOM, события будут продолжать срабатывать в обычном режиме и давать правильные координаты.