Как обнаружить событие touchend вне элемента

я пытаюсь справиться с нажатием кнопки. "Кнопка" пользовательский div, который также может содержать детей. Обратный вызов click должен срабатывать, когда пользователь щелкнул и отпустил внутри элемента. Если пользователь щелкает внутри, а затем перетаскивает и освобождает снаружи, обработчик не должен срабатывать. Мне нужно, чтобы это работало как на рабочем столе, так и на мобильном телефоне, поэтому я использую mousedown/mouseup и touchstart/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 для Android и при нажатии на элемент, а затем вытаскивая, " внутри!"предупреждения.

проблема в этом состоянии:

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

я пытаюсь проверить, является ли touchend событие произошло в кнопке или любом из ребенок. Если кнопка не имеет дочерних элементов, при нажатии на нее, а затем отпуская снаружи, первая часть предложения OR разрешается в true. Если кнопка есть дети и я нажимаю на детей, затем отпустите в любой другой части экрана, вторая часть предложения.

что не так с этим кодом?

спасибо заранее.

обновление
Я тестировал его также в BlackBerry 10 с теми же результатами. Видимо, цель touchend событие-это кнопка (или дети), даже когда я нажал снаружи. Так вот как это должно работать?

обновление
И вот почему. Это что говорят документы W3C о событии:

целью этого события должен быть тот же элемент, который получил событие touchstart, когда эта точка касания была помещена на поверхность, даже если точка касания с тех пор переместилась за пределы интерактивной области цели элемент.

для меня это не имеет смысла, но в любом случае это объясняет мою проблему. Я предполагал touchend событие будет вызываться над элементом, где палец был освобожден. Можно ли обнаружить высвобождение пальца снаружи, используя другой подход?

обновление
Я попытался получить координаты touchevent чтобы получить элемент с помощью document.elementFromPoint. Проблема в том, что это событие не содержит координат (changedTouches и touches списки пустые). Я тороугли проверил событие в отладчике, и нет никакого способа получить из него координаты, кроме исходных событий. Тогда я подумал, что могу спрятать последний touchmove событие и получить координаты оттуда, но опять же это событие не имеет собственных координат! Почему эти два события существуют, когда они бесполезны? И я протестировал этот подход в iOS, Android и BB10 с одинаковыми результатами.

я сдалась. Я перезвоню, даже если ... пользователь щелкнул снаружи. Я не могу поверить, что это 2013 ребята, и нет (простого) способа сделать это? Я мог бы использовать api drag&drop, но в соответствии с этой он не поддерживается на мобильном телефоне (Gotta love mobile web development).

2 ответов


наконец (почти) получил его работу в BB10 и Android. Я оставил след проблем в обновлениях вопроса, поэтому подводя итог:touchend цель события совпадает с touchstart, и он поставляется без координат (дизайнер API почему-то решил "спрятать" их в originalEvent собственность :) . Поэтому я извлекаю координаты сенсорного конца из changedTouches[0].pageX и changedTouches[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 каким-то образом события щелчка дублируются. Но это заслуживает другого вопроса (если он еще не существует).


вам нужен другой и более аккуратный подход все вместе.

реализуйте onmousemove и ontouchmove для настольных и мобильных устройств соответственно.

btn_obj.onmousemove = CancelOnClick (this);

реализуйте функцию CancelOnClick, чтобы установить флаг от нажатия до нормального. Это избавляет вас от всего вашего элемента и его условий, основанных на детях.

надеюсь, что это поможет.