如何找出touchmove javascript事件的实际event.target?

时间:2010-10-12 20:40:50

标签: javascript javascript-events

我正在尝试在我的Web应用程序中开发一个简单的拖放UI。可以通过鼠标或手指拖动项目,然后将其放入多个放置区域中的一个。当项目拖放到放置区域(但尚未释放)时,该区域将突出显示,标记安全着陆位置。这对鼠标事件完全没问题,但我在iPhone / iPad上遇到了touchstart / touchmove / touchend系列。

问题在于,当调用项目的ontouchmove事件处理程序时,其event.touches[0].target始终指向原始HTML元素(项目)而不是当前在手指下的元素。此外,当用手指在某个放置区域上拖动某个项目时,根本不会调用该放置区域自己的touchmove处理程序。这实际上意味着我无法确定手指何时位于任何放置区域之上,因此无法根据需要突出显示它们。同时,使用鼠标时,会为光标下的所有HTML元素正确触发mousedown

有些人确认它应该像那样工作,例如http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/对于那些来自普通网页设计世界的人来说,在正常的mousemove事件中,传递目标属性的节点通常是鼠标当前所在的节点。但在所有iPhone touch事件中,目标是对原始节点的引用。

问题:有没有办法确定手指下的实际元素(不是最初触及的元素,在许多情况下可能会有所不同)?

5 个答案:

答案 0 :(得分:41)

这肯定不是事件目标的工作方式。由于供应商在没有任何审查的情况下在闭门造车的情况下提出扩展,我们现在可能永远陷入了另一个DOM的不一致之处。

使用document.elementFromPoint解决此问题。

document.elementFromPoint(event.clientX, event.clientY);

答案 1 :(得分:34)

2010年接受的答案不再有效:touchmove没有clientXclientY属性。 (我猜它已经习惯了,因为答案有很多赞成,但目前还没有。)

目前的解决方案是:

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

经过测试并继续工作:

  • iOS上的Safari
  • iOS上的Chrome
  • Android上的Chrome
  • 支持触控的Windows桌面上的Chrome
  • 支持触控的Windows桌面上的FF

不适用于:

  • 启用触控的Windows桌面上的IE

未测试:

  • Windows Phone

答案 2 :(得分:4)

我在Android(WebView + Phonegap)上遇到了同样的问题。我希望能够拖动元素并检测它们何时被拖动到某个其他元素上。 由于某些原因,触摸事件似乎忽略了pointer-events属性值。

鼠标:

  • 如果设置了pointer-events="visiblePainted",则event.target将指向拖动的元素。
  • 如果设置了pointer-events="none",那么event.target将指向被拖动元素下的元素(我的拖放区域)

这就是事情应该如何运作以及为什么我们首先拥有pointer-events属性。

触:

  • 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)
        };
    };
    

然后使用它进行处理(检查拖动的对象是否在我的拖放区域中)。出于某种原因,即使在Android上,document.elementFromPoint()似乎也会尊重pointer-events值。

答案 3 :(得分:2)

因此,触摸事件在交互方式上有不同的“哲学”:

  • 鼠标移动= “悬停” 行为
  • 触摸动作= “拖动” 行为

这种差异是由于以下事实造成的:没有没有触摸开始事件的触摸移动,因为用户必须触摸屏才能开始此交互。当然,使用鼠标,用户可以在整个屏幕上进行鼠标移动,而无需按一下臀部(mousedown事件)

这就是为什么我们基本上不希望使用带有触摸的悬停效果之类的原因:

element:hover { 
    background-color: yellow;
}

这就是为什么当用户用一根手指触摸屏幕时,第一个事件(touchstart)将获取目标元素,而后续事件(touchmove)将保留对开始触摸的原始元素的引用。感觉不对,但是有一种逻辑,您可能还需要原始目标信息。因此理想情况下,将来应该同时有(源目标和当前目标)可用。

因此,今天(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")) {
  }

答案 4 :(得分:0)

JSP64 的回答没有完全奏效,因为 event.originalEvent 总是返回 undefined。如下稍作修改即可生效。

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