2013-07-15 5 views
8

Я пытаюсь обработать нажатие кнопки. «Кнопка» - это настраиваемый div, который также может содержать детей. Обратный вызов клика должен срабатывать, когда пользователь щелкнул и выпустил внутри элемента. Если пользователь нажимает внутри, а затем перетаскивает и отпускает снаружи, обработчик не должен запускаться. Мне нужно, чтобы это работало как на рабочем столе, так и на мобильных устройствах, поэтому я использую mousedown/mouseup и touchstart/touchend событий.Как обнаружить событие touchhend вне элемента

Мне нужно изменить класс кнопки от «нажата» к «нормальной», даже если пользователь отпускает снаружи, так что мне нужно добавить слушателя, чтобы захватить рилизинг событие над документом:

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; 
    }); 

It отлично работает с кликами, но с сенсорными событиями он не работает должным образом. Я тестировал это в Chrome для Android и при нажатии над элементом, а затем перетаскивая «Внутри!». отображается предупреждение.

Проблема находится в этом состоянии:

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

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

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

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

ОБНОВЛЕНИЕ
Я тестировал его также в BlackBerry 10 с теми же результатами. Очевидно, целью для события touchend является кнопка (или дети), даже когда я щелкнул снаружи. Так ли это должно работать?

ОБНОВЛЕНИЕ
И вот почему. Это what the W3C docs say about the event:

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

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

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

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

+0

Хотя это может и не помочь вам, я собрал скрипт VanillaJS, который делает почти то же самое, и разместил упрощенную версию этого кода [здесь] (http://stackoverflow.com/questions/13030210/onclick-event-is-occending-two-times-in-iphone-hybrid-app/13030622 # 13030622) –

+0

@EliasVanOotegem спасибо, я прочитал его, но я еще не смог его протестировать. Теперь я читаю источник некоторых из библиотек, которые вы связали. Может быть, я пытаюсь изобрести колесо здесь. –

ответ

8

Наконец-то (почти) он работал в BB10 и Android. Я оставил след в вопросительных обновлениях, поэтому подведем итог: цель события touchend та же, что и touchstart, и она приходит без координат (разработчик API почему-то решил «спрятать» их в originalEvent имущество :) . Поэтому я получаю координаты касания от changedTouches[0].pageX и changedTouches[0].pageY, а затем получаю элемент, в котором отпущен палец с помощью document.elementFromPoint. (Перед подачей этого метода с координатами события вы должны добавить смещение прокрутки к x и y). Тогда, если элемент в этой точке является кнопкой (или является ее дочерним элементом), я обрабатываю щелчок.

Теперь у меня возникла новая проблема: в iOS каким-то образом события щелчка дублируются. Но это заслуживает другого вопроса (если уже не существует).

+1

Проблема с дублированными щелчками была вызвана оповещениями об отладке. Я заменил их 'console.log' и теперь работает как ожидалось. –

+0

Это ужасно! Я чувствую твою боль. –

+0

Координаты должны быть в объекте в списке, называемом targetTouches, который является свойством объекта события, хотя я слышал, что ежевика может быть кошмаром при поддержке спецификации так? там. Это необходимо, потому что вы можете обрабатывать несколько касаний в одном и том же элементе, который может генерировать события в непосредственной близости друг от друга. Это странно, но API имеет гораздо больший смысл, если учесть, что он предназначен для обработки гораздо большего, чем палец, в качестве проблем с заменой мыши и является хорошим дизайном IMO. В библиотеку для абстрагирования материала низкого уровня должно появиться достаточно скоро. –

1

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

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

btn_obj.onmousemove = CancelOnClick (this); 

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

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

+0

'CancelOnClick (this);' присваивает возвращаемое значение 'CancelOnClick' событию' onmousemove', это ужасная идея. –

+1

Это не касается проблемы обнаружения релиза клика над элементом. проблема в том, что мне нужно вызвать обратный вызов, когда заканчивается клик, а не когда он начинается. –

Смежные вопросы