Как узнать фактическое событие event.target события touchmove javascript? - PullRequest
42 голосов
/ 13 октября 2010

Я пытаюсь разработать простой интерфейс перетаскивания в моем веб-приложении. Элемент можно перетащить мышью или пальцем, а затем поместить в одну из нескольких зон выпадения. Когда элемент перетаскивается над зоной сброса (но еще не выпущен), эта зона подсвечивается, отмечая безопасное место посадки. Это прекрасно работает с событиями мыши, но я застрял с семейством touchstart / touchmove / touchend на iPhone / iPad.

Проблема в том, что когда вызывается обработчик события ontouchmove элемента, его event.touches[0].target всегда указывает на исходный элемент HTML (элемент), а не на элемент, который в данный момент находится под пальцем. Более того, когда элемент перетаскивается пальцем через некоторую зону перетаскивания, собственные обработчики touchmove этой зоны перетаскивания вообще не вызываются. По сути, это означает, что я не могу определить, когда палец находится над какой-либо из зон падения, и поэтому не могу выделить их при необходимости. В то же время при использовании мыши mousedown корректно запускается для всех элементов HTML под курсором.

Некоторые люди подтверждают, что это должно работать так, например http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/: Для тех из вас, кто приезжает из обычного мира веб-дизайна, при обычном событии перемещения мышью узел, переданный в атрибуте target, обычно является тем, над чем в данный момент находится мышь. Но во всех событиях касания iPhone целью является ссылка на исходный узел.

Вопрос: есть ли способ определить фактический элемент под пальцем (НЕ первоначально затронутый элемент, который может отличаться во многих обстоятельствах)?

Ответы [ 4 ]

41 голосов
/ 10 декабря 2010

Это определенно не так, как должны работать цели событий. Еще одно несоответствие DOM, с которым мы, вероятно, все теперь застряли навсегда, из-за того, что поставщик придумывает расширения за закрытыми дверями без какого-либо рассмотрения.

Используйте document.elementFromPoint, чтобы обойти это.

document.elementFromPoint(event.clientX, event.clientY);
33 голосов
/ 01 ноября 2015

Принятый ответ от 2010 года больше не работает: touchmove не имеет атрибута clientX или clientY.(Я полагаю, что раньше это так, поскольку в ответе есть несколько голосов "против", но в настоящее время его нет.)

Текущее решение:

var myLocation = event.originalEvent.changedTouches[0];
var realTarget = document.elementFromPoint(myLocation.clientX, myLocation.clientY);

Протестировано и работает на:

  • Safari на iOS
  • Chrome на iOS
  • Chrome на Android
  • Chrome на сенсорном рабочем столе Windows
  • FF вкл.Рабочий стол Windows с сенсорным экраном

НЕ работает на:

  • IE на рабочем столе Windows с сенсорным экраном

Не тестировался на:

  • Windows Phone
4 голосов
/ 22 октября 2013

Я столкнулся с той же проблемой на Android (WebView + Phonegap).Я хочу иметь возможность перетаскивать элементы вокруг и определять, когда они перетаскиваются поверх определенного другого элемента.По некоторым причинам события касания, кажется, игнорируют значение атрибута pointer-events.

Мышь:

  • , если установлено pointer-events="visiblePainted", то event.target будет указывать на перетаскиваемый элемент.
  • , если установлено pointer-events="none", то event.target будет указывать на элемент под перетаскиваемым элементом (моя зона перетаскивания)

Вот как все должно работать ипочему у нас атрибут pointer-events в первую очередь.

Touch:

  • event.target всегда указывает на перетаскиваемый элемент, независимо от значения pointer-events, которое, по-моему, неверно.

Мой обходной путь - создать мой собственный объект события перетаскивания (общий интерфейс для событий мыши и касания), который содержит координаты события и цель:

  • для событий мыши я просто повторно использую событие мыши как
  • для события касания, которое я использую:

    DragAndDrop.prototype.getDragEventFromTouch = function (event) {
        var touch = event.touches.item(0);
        return {
            screenX: touch.screenX,
            screenY: touch.screenY,
            clientX: touch.clientX,
            clientY: touch.clientY,
            pageX: touch.pageX,
            pageY: touch.pageY,
            target: document.elementFromPoint(touch.screenX, touch.screenY)
        };
    };
    

, а затем использую его для обработки (проверяя,перетаскиваемый объект находится в моей зоне перетаскивания).По какой-то причине document.elementFromPoint(), похоже, соответствует значению pointer-events даже на Android.

2 голосов
/ 08 августа 2018

Так что у сенсорных событий разная «философия», когда речь заходит о том, как они взаимодействуют:

  • Мышь движется = "зависание" как поведение
  • Касание движется = "перетаскивает" как поведение

Эта разница обусловлена ​​тем фактом, что не может быть движением касания без события touchstart, предшествующего ему, поскольку пользователь должен коснуться экрана, чтобы начать это взаимодействие. Конечно, с помощью мыши пользователь может перемещать мышь по всему экрану, даже не нажимая кнопку (событие mousedown)

Вот почему в основном мы не можем надеяться использовать такие вещи, как эффекты при наведении, с сенсорным:

element:hover { 
    background-color: yellow;
}

И именно поэтому, когда пользователь касается экрана одним пальцем, первое событие (сенсорный запуск) получает целевой элемент, а последующие события (сенсорное движение) будут содержать ссылку на исходный элемент, с которого началось касание. Это неправильно, но есть логика, что вам может понадобиться и оригинальная информация о цели. Так что в идеале в будущем должны быть доступны и исходная цель, и текущая цель.

Так что обычная практика сегодняшнего дня (2018), когда экраны могут быть одновременно мышкой и сенсорным, все еще заключается в том, чтобы прикреплять обоих слушателей (мышку и касание), а затем «нормализовать» координаты события и использовать вышеупомянутые API браузера для поиска элемента в эти координаты:

  // get coordinates depending on pointer type:
  var xcoord = event.touches? event.touches[0].pageX : event.pageX;
  var ycoord = event.touches? event.touches[0].pageY : event.pageY;
  // get element in coordinates:
  var targetElement = document.elementFromPoint(xcoord, ycoord);
  // validate if this is a valid element for our case:
  if (targetElement && targetElement.classList.contains("dropZone")) {
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...