如何让touchEnd的行为与mouseUp相同?

15

mouseUp 或者 touchEnd 事件发生时,我想获取发生事件的元素的引用。

Demo

在这个例子中,你需要点击一个元素,拖动鼠标并释放在另一个元素上。当鼠标释放时,那个元素的背景颜色将变成红色。我的代码对于鼠标事件是正确的,但在触摸设备上无法正常工作。

touchStart 在一个元素上,手指释放在另一个元素上时,我只能得到第一个元素的引用。

我需要如何改变代码才能使触摸事件的行为与鼠标事件完全一致?

var isMouseDown = false;    


var panel1 = document.getElementById( 'panel1' );
var panel2 = document.getElementById( 'panel2' );

panel1.onmousedown = onDocumentMouseDown;
panel1.onmouseup = onDocumentMouseUp;
panel1.onmousemove = onDocumentMouseMove;   
panel1.addEventListener( 'touchstart', onDocumentTouchStart, false );
panel1.addEventListener( 'touchmove', onDocumentTouchMove, false );
panel1.addEventListener( 'touchend', onDocumentTouchEnd, false );

panel2.onmousedown = onDocumentMouseDown;
panel2.onmouseup = onDocumentMouseUp;
panel2.onmousemove = onDocumentMouseMove;
panel2.addEventListener( 'touchstart', onDocumentTouchStart, false );
panel2.addEventListener( 'touchmove', onDocumentTouchMove, false );
panel2.addEventListener( 'touchend', onDocumentTouchEnd, false );



function onDocumentMouseDown(event) {
    event.preventDefault();
    panel1.style.background="#2E442E";
    panel2.style.background="#5D855C";      
}

function onDocumentMouseUp(event) {
    event.preventDefault();
    this.style.background="#ff0000";
}

function onDocumentMouseMove( event ) {

}   

function onDocumentTouchStart( event ) {
    event.preventDefault();
    panel1.style.background="#2E442E";
    panel2.style.background="#5D855C";

}

function onDocumentTouchMove( event ) {     

}

function onDocumentTouchEnd( event ) {
    event.preventDefault();         
    this.style.background="#ff0000";        

}

你尝试过哪些触摸设备? - shareef
类似的问题:如何查找touchmove javascript事件的实际event.target?(https://dev59.com/YG865IYBdhLWcg3wOcDy) - Rohit
3个回答

14
这是一个有趣的问题。感谢您提出。这是我所做的。我已将您的 touchmove 处理程序修改为以下内容:
   function onDocumentTouchMove(event) {
    onDocumentTouchMove.x = event.changedTouches[event.changedTouches.length - 1].clientX;
    onDocumentTouchMove.y = event.changedTouches[event.changedTouches.length - 1].clientY;
}
在这个处理程序中,我保存了用户移动到的最后一个坐标。很可能,这是用户将手指、鼻子、指关节、笔等从触摸面板上拿开的点。我在touchend处理程序中使用这些坐标来找到围绕它们的元素。
    function onDocumentTouchEnd(event) {
    event.preventDefault();
    var elem = document.elementFromPoint(onDocumentTouchMove.x, onDocumentTouchMove.y);
    elem.style.background = "#ff0000";
}

我已经利用了document.elementFromPoint(mdn链接)来实现这个目的。它是由CSSOM规范提供给我们的那些金科玉律但相对不太知名的方法之一。

这里是JSFiddle示例


1
太好了!我可以确认在三星Galaxy Tab上使用Chrome是有效的。 - Justus Romijn
也适用于我的One X上的Opera、原生Android浏览器和Chrome。 - zeusdeux
是的,我认为这是一个不错的解决方案.. :) 但我不知道elementFromPoint的支持情况。 - Sarath
1
@SarathSaleem support for elementFromPoint 来自 quirksmode - zeusdeux
谢谢,它起作用了。但是在没有悬停的简单点击上它不起作用,所以我不得不将onDocumentTouchMove()绑定到touchstart事件上。 - BoltKey

0

从看起来的情况来看,这是触摸事件的预期行为。但是,我有一个解决方法!

我没有进行过广泛的测试,但至少在移动Safari上似乎适用于touchStarttouchEnd事件。

基本上,我们要做的就是在文档上捕获所有触摸事件,因为它们处于捕获阶段(参见http://www.w3.org/TR/DOM-Level-3-Events/)。这些事件的处理程序将找到此位置的DOM元素,并重新发送具有“正确”目标的事件。

似乎touchEnd事件的位置没有很好地定义,因此我必须查询changedEvents数组以获取最后触摸的位置。我不确定这是否适用于touchMove等事件。

function rerouteTouch (evt) {
    if (evt.passthrough) { // Ignore if already rerouted
        return;
    }
    evt.stopPropagation();
    var newevt = document.createEvent('TouchEvent');
    newevt.initTouchEvent (
        evt.type,
        evt.bubbles,
        evt.cancelable,
        evt.view,
        evt.detail,
        evt.screenX,
        evt.screenY,
        evt.clientX,
        evt.clientY,
        evt.ctrlKey,
        evt.altKey,
        evt.shiftKey,
        evt.metaKey,
        evt.touches,
        evt.targetTouches,
        evt.changedTouches,
        evt.scale,
        evt.rotation);
    newevt.passthrough = true; // Only reroute once

    var target;
    var x = (evt.type === 'touchend') ? evt.changedTouches[0].pageX : evt.pageX;
    var y = (evt.type === 'touchend') ? evt.changedTouches[0].pageY : evt.pageY;

    if (document !== (target = document.elementFromPoint(x, y))) {
        target.dispatchEvent(newevt);
    }
}

document.addEventListener( 'touchstart', rerouteTouch, true); // Third arg is true to indicate capture-phase
document.addEventListener( 'touchend', rerouteTouch, true); 

...

// Your event handlers here

这里有一个 jsfiddle,对于我来说在移动版 Safari 上的效果符合预期:http://jsfiddle.net/DREer/12/


-1
也许如果你能将jQuery包含到你的程序中,你就可以像这样做:
$('#itemId').on('touchEnd',function(){ this.trigger('mouseUp') });

我不确定jQuery是否原生支持这些事件,但你可以尝试使用jQuery movil,希望能帮到你。


@isaac 这里的 this 仍然是 touchstart 被触发的元素,因此它不是一个可行的解决方案。 - zeusdeux

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接