Как обнаружить событие 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, чтобы установить флаг от нажатия до нормального. Это избавляет вас от всего вашего элемента и его условий, основанных на детях.
надеюсь, что это поможет.