触摸事件处理程序重写点击处理程序 [英] Touch event handler overrides click handlers

查看:152
本文介绍了触摸事件处理程序重写点击处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建的AngularJS自定义可拖动的指令。这是jQuery的事件和香草javascript组合。我试图使此作为通用的和可重复使用成为可能,并且它也必须触摸友好。

I am creating a custom draggable directive in AngularJS. It's a combination of jQuery events and vanilla javascript. I am trying to make this as generic and re-useable as possible, and it also has to be touch friendly.

TL; DR

我不能点击我可拖动的指令按钮接触的环境中。

I can't click the button on my draggable directive in touch environments.

重现步骤:


  1. 打开codePEN例如: codePEN

  2. 在浏览器,F12,模拟iPad的3/4

  3. 将标题面板拖动=作品!

  4. 单击该按钮=警报。

较长的解释

该指令任选使整个元件它放置在拖动,除非与类拖柄的元素被放置,在这种情况下,用于作为拖动手柄的元素。我通常与引导板使用,因为它是一个简单的例子。

The directive optionally makes the entire element it's placed on draggable, unless an element with the class "drag-handle" is placed, in which case that is used as the drag handle for the element. I normally use this with bootstrap panels, as it's an easy example.

该指令适用于台式机大,但是在触摸设备上,如果有一个拖动手柄的可点击的内容,拖动处理程序重写click事件和它永远不会被调用。

The directive works great on desktop, however on touch devices, if there are any clickable items on a drag handle, the drag handler overrides the click event and it's never called.

示例HTML是:

<div class="panel panel-default" app-draggable>
    <div class="panel-heading drag-handle"> <!-- Drag Handle -->
        <div class="panel-title">
            Example Title
            <button onclick="alert('clicked')" class="btn btn-xs btn-primary pull-right" type="button">Click</button>
        </div>
    </div>
    <div class="panel-body">Example body</div>
</div>

因此​​在桌面上,你既可以拖动面板,然后单击按钮获得警报。然而,当我模仿iPad的3/4在Chrome(或拉起一个真正的iPad)点击是永远不会被解雇了。

So on desktops, you can both drag the panel, and click the button to get the alert. However, when I emulate iPad 3/4 on Chrome (or pull it up on a real iPad) the click is never fired.

我的指令如下。它设置在容器是绝对的(除非容器已经是固定的,在这种情况下,它将补偿,仍然使拖拽

My directive is below. It sets the container to be absolute (unless the container is already fixed, in which case it will compensate and still make it draggable.

 /*
 * @summary
 * Directive that makes an element draggable.
 * @description
 * This directive should be used in conjunction with specifying a drag handle 
 * on the element. If not, then entire element will be draggable.
 * @example
 * <div class='myDiv' app-draggable>
 *   <div class='drag-handle'>This will be the drag handle</div>
 *   <div>This will be dragged</div>
 * </div>
 */
angular.module("app")
    .directive('appDraggable', appDraggable);

function appDraggable() {
    var directive = {
        restrict: 'A',
        link: link
    };

    function link(scope, element) {
        var startX = 0, startY = 0, x = 0, y = 0;
        var startTop;
        var startLeft;
        var dragHandle = element[0].querySelector(".drag-handle");
        var dragHandleElement;
        /*
         * If there is a dragHandle specified, add the touch events to it.
         * Otherwise, make the entire element draggable.
         */ 
        if (dragHandle) {
            dragHandleElement = angular.element(dragHandle);
            addTouchHandlers(dragHandle);
        } else {
            dragHandleElement = element;
            addTouchHandlers(element[0]);
        }

        var position = element.css('position');

        if (position !== "absolute") {
            if (position === "fixed") {
                // If fixed, get the start offset relative to the document.
                startTop = element.offset().top;
                startLeft = element.offset().left;
                /*
                 * Explicitly set the height and width of the element to prevent
                 * overrides by preset values.
                 */ 
                var height = parseInt(element.height(), 10);
                var width = parseInt(element.width(), 10);
                element.css({
                    height: height,
                    width: width
                });
            } else {
                // If it's not fixed, it needs to be absolute.
                element.css({
                    position: 'absolute',
                });
                // And positioned originally relative to the parent.
                startTop = element.position().top;
                startLeft = element.position().left;
            }
        }


        /*
         * @function
         * @description
         * Add event handlers to the drag handle to capture events.
         */
        dragHandleElement.on('mousedown', function (event) {

            /*
             * Prevent default dragging of selected content
             */
            event.preventDefault();
            startX = event.pageX - x;
            startY = event.pageY - y;
            dragHandleElement.on('mousemove', mousemove);
            dragHandleElement.on('mouseup', mouseup);
        });

        function mousemove(event) {

            y = event.pageY - startY;
            x = event.pageX - startX;
            var finalTop = y + startTop;
            var finalLeft = x + startLeft;
            element.css({
                top: finalTop + 'px',
                left: finalLeft + 'px'
            });
        }

        function mouseup() {
            dragHandleElement.off('mousemove', mousemove);
            dragHandleElement.off('mouseup', mouseup);
        }

        function touchHandler(event) {
            var touch = event.changedTouches[0];

            if (event.target !== dragHandleElement) {
                //////////////// HACK ///////////////////////////
                //event.target.click(); // Hack as a work around.
            }

            var simulatedEvent = document.createEvent("MouseEvent");
            simulatedEvent.initMouseEvent({
                touchstart: "mousedown",
                touchmove: "mousemove",
                touchend: "mouseup"
            }[event.type], true, true, window, 1,
            touch.screenX, touch.screenY,
            touch.clientX, touch.clientY, false,
            false, false, false, 0, null);

            touch.target.dispatchEvent(simulatedEvent);
            event.preventDefault();
        }

        function addTouchHandlers(element) {
            element.addEventListener("touchstart", touchHandler, true);
            element.addEventListener("touchmove", touchHandler, true);
            element.addEventListener("touchend", touchHandler, true);
            element.addEventListener("touchcancel", touchHandler, true);
        }


    }
    return directive;
}

您会发现,有在上面的指令一个黑客:

You'll notice that there's a hack in the directive above:

if (event.target !== dragHandleElement) {
     //////////////// HACK ///////////////////////////
     //event.target.click(); // Hack as a work around.
}

如果我取消这一点,它适用于触摸设备,因为这会检查是否触摸圆盾是dragHandle,如果它不是,请手动点击目标。这工作,但似乎讨厌我,我真的想更好的解决方案。因为目标直接并非总是dragHandle它不会返回false或stopPropagation,但它仍然需要进行拖动。

If I uncomment this, it works on touch devices because this checks to see if the touch targe is the dragHandle and if it's not, manually clicks the target. This works, but seems nasty to me and I'd really like a better solution. It does not return false or stopPropagation because the target is not always the dragHandle directly, but it still needs to drag.

我不知道为什么这不起作用,因为它没有手动停止触摸事件的传播,因为它使用的事件。preventDefault代替event.stopPropagation,但我敢肯定,我M失去了一些东西。

I don't know why this doesn't work, because it doesn't manually stop the propagation of the touch event, as it uses event.preventDefault instead of event.stopPropagation, but I'm sure I'm missing something.

您可以复制 rel=\"nofollow\">。

You can reproduce here.

此外,关于如何提高上述code的任何其他建议要多平台设备无关的或更稳健的欢迎!

Also, any other recommendations on how to improve the above code to be more platform device agnostic or more robust are welcome!

思考?

谢谢!

推荐答案

发现问题。

以上我的 touchHandler 函数总是传递触摸一个鼠标按下事件,即使是更准确的单击事件应该传递。因为我所有的事件处理程序正在寻找一个click事件,他们忽略了正在传输的鼠标按下事件。

My touchHandler function above always transmits a "mousedown" event on touch, even if it was more accurately a "click" event it should be transmitting. Since all my event handlers were looking for a "click" event, they were ignoring the "mousedown" event that was being transmitted.

我改变了我的 touchHandler 函数的下方,它的工作原理就像一个魅力。

I changed up my touchHandler function to the below and it works like a charm.

    var mouseMoved = false;
    function touchHandler(event) {
        // Declare the default mouse event.
        var mouseEvent = "mousedown";
        // Create the event to transmit.
        var simulatedEvent = document.createEvent("MouseEvent");

        switch (event.type) {
        case "touchstart":
            mouseEvent = "mousedown";
            break;
        case "touchmove":
            /*
            * If this has been hit, then it's a move and a mouseup, not a click
            * will be transmitted.
            */
            mouseMoved = true;
            mouseEvent = "mousemove";
            break;
        case "touchend":
            /*
            * Check to see if a touchmove event has been fired. If it has
            * it means this have been a move and not a click, if not
            * transmit a mouse click event.
            */
            if (!mouseMoved) {
                mouseEvent = "click";
            } else {
                mouseEvent = "mouseup";
            }
            // Initialize the mouseMove flag back to false.
            mouseMoved = false;
            break;
        }

        var touch = event.changedTouches[0];

        /*
         * Build the simulated mouse event to fire on touch events.
         */
        simulatedEvent.initMouseEvent(mouseEvent, true, true, window, 1,
        touch.screenX, touch.screenY,
        touch.clientX, touch.clientY, false,
        false, false, false, 0, null);

        /*
         * Transmit the simulated event to the target. This, in combination
         * with the case statement above, ensures that click events are still
         * transmitted and bubbled up the chain.
         */
        touch.target.dispatchEvent(simulatedEvent);

        /*
         * Prevent default dragging of element.
         */
        event.preventDefault();
    }

这实现查找之间的 touchstart touchend touchmove 事件code>。如果有,那么它设置一个标志并发送点击事件,而不是一个鼠标按下事件。

This implementation looks for a touchmove event in between the touchstart and touchend. If there is one, then it sets a flag and transmits a click event instead of a mousedown event.

它也结合使用具有定时器,使得即使鼠标被移动少量它将发送一个点击事件,但我的目的本奇妙的作品。

It could also be used in conjunction with a timer so that even if the mouse was moved a small amount it would transmit a click event, but for my purposes this works wonderfully.

这篇关于触摸事件处理程序重写点击处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆