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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3918842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:38:01  来源:igfitidea点击:

How to find out the actual event.target of touchmove javascript event?

javascriptjavascript-events

提问by Ilya Semenov

I am trying to develop a simple drag/drop UI in my web application. An item can be dragged by a mouse or a finger and then can be dropped into one of several drop zones. When an item is dragged over a drop zone (but not yet released), that zone is highlighted, marking safe landing location. That works perfectly fine with mouse events, but I'm stuck with touchstart/touchmove/touchend family on the iPhone/iPad.

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

The problem is that when an item's ontouchmove event handler is called, its event.touches[0].targetalways points to the originating HTML element (the item) and not the element which is currently under the finger. Moreover, when an item is dragged by finger over some drop zone, that drop zone's own touchmovehandlers isn't called at all. That essentially means I can't determine when a finger is above any of the drop zones, and therefore can't highlight them as needed. At the same time, when using a mouse, mousedownis correctly fired for all HTML elements under the cursor.

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

Some people confirm that it's supposed to work like that, for instance http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/: For those of you coming from the normal web design world, in a normal mousemove event, the node passed in the target attribute is usually what the mouse is currently over. But in all iPhone touch events, the target is a reference to the originating node.

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

Question:is there any way to determine the actual element under a finger (NOT the initially touched element which can be different in many circumstances)?

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

采纳答案by Glenn Maynard

That's certainly not how event targets are supposed to work. Yet another DOM inconsistency that we're probably all now stuck with forever, due to a vendor coming up with extensions behind closed doors without any review.

这当然不是事件目标应该如何工作。另一个 DOM 不一致性,我们现在可能永远都被困住了,因为供应商在没有任何的情况下提出了闭门造车的扩展。

Use document.elementFromPointto work around it.

使用document.elementFromPoint解决它。

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

回答by JSP64

The accepted answer from 2010 no longer works: touchmovedoes not have a clientXor clientYattribute. (I'm guessing it used to since the answer has a number of upvotes, but it doesn't currently.)

2010 年接受的答案不再有效:touchmove没有clientXorclientY属性。(我猜它曾经有过,因为答案有很多赞成票,但目前没有。)

Current solution is:

目前的解决方案是:

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

Tested and works on:

经过测试并适用于:

  • Safari on iOS
  • Chrome on iOS
  • Chrome on Android
  • Chrome on touch-enabled Windows desktop
  • FF on touch-enabled Windows desktop
  • iOS 上的 Safari
  • iOS 上的 Chrome
  • 安卓上的 Chrome
  • 支持触控的 Windows 桌面上的 Chrome
  • 支持触控的 Windows 桌面上的 FF

Does NOT work on:

不适用于:

  • IE on touch-enabled Windows desktop
  • 支持触控的 Windows 桌面上的 IE

Not tested on:

未测试:

  • Windows Phone
  • 视窗电话

回答by Sergey Rudenko

So touch events have different "philosophy" when it comes to how they interact:

因此,当涉及到如何交互时,触摸事件具有不同的“哲学”:

  • Mouse moves = "hover"like behavior
  • Touch moves = "drags"like behavior
  • 鼠标移动 = “悬停”之类的行为
  • 触摸移动 = “拖动”之类的行为

This difference comes from the fact that there can not bea touchmove without touchstart event preceding it as a user has to touch screen to start this interaction. With mouse of course a user can mousemove all over the screen without ever pressing buttoon (mousedown event)

这种差异来自这样一个事实,即不能在没有 touchstart 事件的情况下进行 touchmove,因为用户必须触摸屏幕才能开始此交互。使用鼠标,用户当然可以在整个屏幕上移动鼠标而无需按下按钮(mousedown 事件)

This is why basically we can't hope to use things like hover effects with touch:

这就是为什么基本上我们不能希望使用触摸悬停效果之类的东西:

element:hover { 
    background-color: yellow;
}

And this is why when user touches the screen with 1 finger the first event (touchstart) acquires the target element and the subsequent events (touchmove) will hold the reference to the original element where touch started. It feels wrong but there is this logic that you might need original target info as well. So ideally in future there should be both (source target and current target) available.

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

So common practice of today (2018) where screens can be mouse AND touch at the same time is still to attach both listeners (mouse and touch) and then "normalize" event coordinates and use above mentioned browser api to find element in those coordinates:

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

回答by Jan Zarnikov

I've encountered the same problem on Android (WebView + Phonegap). I want to be able to drag elements around and detect when they are being dragged over a certain other element. For some reason touch-events seem to ignore the pointer-eventsattribute value.

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

Mouse:

鼠:

  • if pointer-events="visiblePainted"is set then event.targetwill point to the dragged element.
  • if pointer-events="none"is set then event.targetwill point to the element under the dragged element (my drag-over zone)
  • 如果pointer-events="visiblePainted"设置,event.target则将指向拖动的元素。
  • 如果pointer-events="none"设置然后event.target将指向拖动元素下的元素(我的拖动区域)

This is how things are supposed to work and why we have the pointer-eventsattribute in the first place.

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

Touch:

触碰:

  • event.targetalways points to the dragged element, regardless of pointer-eventsvalue which is IMHO wrong.
  • event.target始终指向拖动的元素,无论pointer-events恕我直言错误的值如何。

My workaround is to create my own drag-event object (a common interface for both mouse and touch events) that holds the event coordinates and the target:

我的解决方法是创建我自己的拖动事件对象(鼠标和触摸事件的通用界面)来保存事件坐标和目标:

  • for mouse events I simply reuse the mouse event as is
  • for touch event I use:

    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)
        };
    };
    
  • 对于鼠标事件,我只是按原样重用鼠标事件
  • 对于触摸事件,我使用:

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

And then use that for processing (checking whether the dragged object is in my drag-over zone). For some reason document.elementFromPoint()seems to respect the pointer-eventsvalue even on Android.

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