12

我正在尝试处理按钮单击。“按钮”是一个自定义 div,也可以包含子项。当用户在元素内点击和释放时,点击回调应该触发。如果用户在内部单击然后在外部拖动和释放,则处理程序不应触发。我需要让它在桌面和移动设备上都能正常工作,因此我正在使用mousedown/mouseuptouchstart/touchend事件。

即使用户在外面释放,我也需要将按钮类从“按下”更改为“正常”,因此我需要添加一个侦听器来捕获文档上的释放事件:

    var $myElement = $("#myelement"); //This is a div and can also contain children.

    $myElement.on("mousedown touchstart", function(e){  

        $(this).addClass("pressed");    

        $(document).on("mouseup touchend", function listener(e){
            $myElement.removeClass("pressed");

            $(document).off("mouseup touchend", listener);

            var $target = $(e.target);
            if ($target.is($myElement) || $target.parents().is($myElement)){        
                //FIXME
                alert("Inside!");

                //do something here
                return false;
            } else {
                //FIXME
                alert("Outside!");

                //let event bubble
            }
        });

        return false;
    });

它在点击时可以正常工作,但在触摸事件中不能按预期工作。我已经在 Chrome for Android 中对此进行了测试,当按下元素时,然后拖出“内部!” 显示警报。

问题是在这种情况下:

if ($target.is($myElement) || $target.parents().is($myElement)){

我正在尝试检查touchend事件是否发生在按钮或任何孩子中。如果按钮没有子按钮,当单击它然后在外部释放时,OR 子句的第一部分解析为 true。如果按钮有子项并且我在子项中单击,然后在屏幕的任何其他部分释放,则子句的第二部分为真。

这段代码有什么问题?

提前致谢。

更新
我也在 BlackBerry 10 中对其进行了测试,结果相同。显然,touchend事件的目标是按钮(或孩子),即使我点击了外面。这是它应该如何工作的吗?

更新
这就是原因。这就是W3C 文档关于该事件的说法

此事件的目标必须与在此触摸点放置在表面上时接收到 touchstart 事件的元素相同,即使该触摸点已经移出目标元素的交互区域。

这对我来说毫无意义,但无论如何这解释了我的问题。我假设该touchend事件将在手指被释放的元素上调用。是否可以使用不同的方法检测到手指在外面的释放?

更新
我试图获取 的坐标touchevent以使用document.elementFromPoint. 问题是这个事件不包含坐标(changedTouchestouches列表是空的)。我已经在调试器中彻底检查了事件,除了原始事件之外,没有办法从中检索坐标。然后我想我可以缓存最后一个touchmove事件并从那里获取坐标,但是这个事件又没有自己的坐标!为什么这两个事件在它们无用的情况下仍然存在?我已经在 iOS、Android 和 BB10 中测试了这种方法,结果相同。

我已经放弃了。即使用户在外面点击,我也会调用回调。我不敢相信这是 2013 年的家伙,而且没有(简单的)方法可以做到这一点?我可以使用拖放 api,但据此在移动设备上不受支持(必须喜欢移动 Web 开发)。

4

2 回答 2

10

最后(几乎)让它在 BB10 和 Android 上运行。我在问题更新中留下了一系列问题,所以总结一下:touchend事件目标与 相同touchstart,并且它没有坐标(API设计人员出于某种原因决定将它们“隐藏”在originalEvent属性中:) . 因此,我从中检索触摸结束坐标changedTouches[0].pageXchangedTouches[0].pageY然后使用document.elementFromPoint. (在向此方法提供事件坐标之前,您必须将滚动偏移量添加到 x 和 y)。然后,如果该点的元素是按钮(或者是它的子元素),我会处理点击。

    var $myElement = $("#myelement"); //This is a div and can also contain children.
    var startEvent = touchDevice() ?  "touchstart" : "mousedown";
    var releaseEvent = touchDevice() ?  "touchend" : "mouseup";

    $myElement.on(startEvent, function(e){  

        $(this).addClass("pressed");    

        $(document).on(releaseEvent, function listener(e){
            $myElement.removeClass("pressed");

            $(document).off(releaseEvent, listener);

            var $target = null;
            if(e.type === "mouseup"){
                 $target = $(e.target);         
            } else {            
                var x = e.originalEvent.changedTouches[0].pageX - window.pageXOffset;
                var y = e.originalEvent.changedTouches[0].pageY - window.pageYOffset;
                var target = document.elementFromPoint(x, y);

                if(target){
                    $target = $(target);
                }           
            }

            if ($target !== null && ($target.is($myElement) || $target.parents().is($myElement))){      
                //FIXME
                alert("Inside!");

                //callback here
                return false;
            } else {
                //FIXME
                alert("Outside!");
            }
        });

        return false;
    });

现在我遇到了一个新问题:在 iOS 中,点击事件以某种方式被复制了。但这值得提出一个不同的问题(如果不存在的话)。

于 2013-07-15T12:55:41.797 回答
1

你需要一种不同的、更整洁的方法。

分别为桌面设备和移动设备实现 onmousemove 和 ontouchmove。

btn_obj.onmousemove = CancelOnClick (this);

实现 CancelOnClick 函数将标志从按下设置为正常。它将您从所有元素及其基于子项的条件中解救出来。

希望有帮助。

于 2013-07-15T09:09:41.853 回答