Как связать события «touchstart» и «click», но не отвечать на оба?

198

Я работаю над мобильным веб-сайтом, который должен работать на разных устройствах. В настоящий момент у меня болит голова, это BlackBerry.

Нам нужно поддерживать как нажатия клавиш, так и сенсорные события.

В идеале я бы просто использовал:

$thing.click(function(){...})

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

Вместо этого можно использовать Touchstart:

$thing.bind('touchstart', function(event){...})

Но как мне связать оба события, но только одно? Мне все еще нужно событие click для клавиатурных устройств, но, разумеется, я не хочу, чтобы событие click срабатывало, если я использую сенсорное устройство.

Дополнительный вопрос: есть ли возможность сделать это и дополнительно разместить браузеры, у которых даже нет события сенсорного запуска? При исследовании этого, похоже, что BlackBerry OS5 не поддерживает сенсорный запуск, поэтому для этого браузера также необходимо полагаться на события нажатия.

ДОПОЛНЕНИЕ:

Возможно, более полный вопрос:

С jQuery возможно / рекомендуется обрабатывать как прикосновения, так и взаимодействия мыши с одними и теми же привязками?

В идеале ответ - да. Если нет, у меня есть несколько вариантов:

1) Мы используем WURFL для получения информации об устройстве, чтобы создать собственную матрицу устройств. В зависимости от устройства мы будем использовать сенсорный запуск или щелчок.

2) Обнаружение сенсорной поддержки в браузере с помощью JS (мне нужно провести дополнительное исследование по этому вопросу, но кажется, что это выполнимо).

Однако это все еще оставляет одну проблему: как насчет устройств, которые поддерживают ОБА. Некоторые из поддерживаемых нами телефонов (а именно Nokias и BlackBerries) имеют сенсорные экраны и клавиатуры. Так что я снова возвращаюсь к первоначальному вопросу ... есть ли способ как-то учесть оба сразу?

DA.
источник
2
Вам лучше связываться с touchstart и touchend и писать свою собственную логику щелчков вместе с логикой касания. Встроенный обратный вызов щелчка, так как нет знания касаний.
Джастин808
Я не уверен, что следую, Джастин. Разве у меня не было бы связанного с ним и сенсорного запуска, и события клика?
DA.
@DA - нет, вы вообще не будете связываться с обратным вызовом .click (). Я постараюсь написать ответ в некотором коде sudo. У меня нет сенсорного устройства, удобного для написания реального кода :)
Justin808
Ах, но чтобы уточнить, мне все еще нужны события кликов, так как будут люди, получающие доступ к этому сайту с помощью устройств без сенсорного экрана.
DA.
2
Использование .bind('touchstart mouseup')решит это (основываясь на одном из комментариев ниже)
oriadam

Ответы:

135

Обновление : ознакомьтесь с проектом Polyfill jQuery Pointer Events, который позволяет вам связывать события «указателя» вместо выбора между мышью и касанием.


Привязать к обоим, но сделать флаг, чтобы функция срабатывает только один раз в 100 мс или около того.

var flag = false;
$thing.bind('touchstart click', function(){
  if (!flag) {
    flag = true;
    setTimeout(function(){ flag = false; }, 100);
    // do something
  }

  return false
});
Mottie
источник
7
хм ... это кажется хакерским, но это может сработать. Загвоздка в том, что на некоторых из этих устройств это заметное отставание ... возможно, почти секунда ... что будет раздражать других на более быстром устройстве.
DA.
15
Вместо использования clickизмените его на mousedown... может быть, большая задержка - это разница между mousedownи тем, mouseupкак clickопределяется a .
Мотти
7
Это решение, которое я выбрал. Спасибо! Что я делаю, так это отмечаю элемент touchend, применяя класс touched. Это срабатывает до вызова события click. Затем я добавляю событие click, которое сначала проверяет существование этого класса. Если он есть, мы предполагаем, что сенсорные события сработали, и мы ничего не делаем с щелчком, кроме удаления имени класса, чтобы «сбросить» его для следующего взаимодействия. Если класса нет, то мы предполагаем, что они использовали клавиатуру, чтобы щелкнуть по элементу, и запустить функцию оттуда.
DA.
7
Вы должны знать, что большинство браузеров телефонов имеют задержку 300 мс на событие click, поэтому вам следует увеличить время удаления как минимум до 300. См. Эту статью о проблеме задержки 300 мс . Это в основном для включения функции «двойного щелчка для увеличения», которая была очень важной функцией в первые дни iPhone, когда большинство веб-сайтов предназначались для просмотра на большом экране рабочего стола.
благосклонность
12
Этот вопрос был просмотрен около 100 000 раз на данный момент. Это похоже на необычайно распространенный вариант использования / проблему. Тем не менее, этот и другие ответы на этот и другие подобные вопросы кажутся хакерскими. Решение Google, хотя и более тщательно продуманное, чем большинство, кажется просто более надежным хакером. Для чего-то такого простого, как обработчик кликов, кажется, что решение должно быть простым. Никто не нашел не взломать решение этой проблемы?
jinglesthula
73

Это исправление, которое я «создаю», оно извлекает GhostClick и реализует FastClick. Попробуйте сами и сообщите нам, сработало ли это для вас.

$(document).on('touchstart click', '.myBtn', function(event){
        if(event.handled === false) return
        event.stopPropagation();
        event.preventDefault();
        event.handled = true;

        // Do your magic here

});
Рафаэль Фрагосо
источник
2
helgatheviking: jsbin.com/ijizat/25 В JavaScript есть функция TouchClick, которая включает в себя все вышеперечисленное.
Мэтт Паркинс
5
Я НАСТОЯТЕЛЬНО предпочитаю этот метод из всех ответов на этот вопрос, потому что он а) не тестирует возможности устройства и б) не использует setTimeout. По моему опыту, решения таких проблем, которые используют setTimeout, могут работать в большинстве случаев, но время событий довольно произвольно и зависит от устройства.
itsmequinn
7
Это не сработает, потому что, передавая «событие» в анонимной функции, оно становится локальным объектом внутри этой функции, поэтому event.handledбудет равняться undefinedкаждый раз, когда событие инициируется. Вот решение: установите флаг handled в самом целевом элементе, используя jQuery.data().
thdoan
3
Вы event.stopPropagation();и, event.preventDefault();насколько я понимаю (и тестирую), событие может быть запущено только один раз. так зачем даже проверять if(event.handled !== true)? Для меня это не нужно или я что-то упускаю?
nbar
2
Не должны event.handledбыть проверены на значение true? Насколько я могу сказать, это никогда false. Это начинается как undefined, и затем вы хотите сказать, было ли это установлено в true.
ACJ
49

Вы можете попробовать что-то вроде этого:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
swooter
источник
5
Я думаю, проблема в том, что он тестирует возможности устройства, а не то, что делает пользователь, верно? Задача состоит в том, чтобы поддерживать сенсорные устройства, которые также имеют клавиатуры (чтобы они могли использовать оба)
DA.
8
Это не очень хорошая идея - сломать событие для пользователя с сенсорным экраном и мышью при использовании мыши (ноутбуки с сенсорными экранами становятся довольно распространенным явлением).
Кристиан Дж.
1
Отличная статья, связанная с этим: hacks.mozilla.org/2013/04/…
Люк Мелия
Windows 8 всегда будет подключать событие «touchstart», если оно используется, потому что «ontouchstart» вернет «true» для этой ОС. Так что, вероятно, не самый лучший ответ, если только ваш код не будет работать только на устройствах с сенсорным экраном, но если это так, то вам вообще не нужна проверка.
Нил Монро
На ноутбуках с сенсорным экраном пользователь не сможет ничего нажимать, используя этот код в Chrome (по крайней мере, Chrome 46). IE 11, кажется, работает, хотя.
Дэвид Шеррет
42

Обычно это работает также:

$('#buttonId').on('touchstart click', function(e){
    e.stopPropagation(); e.preventDefault();
    //your code here

});
Джонатан
источник
8
@ Джонатан не прав, если вы используете более новые версии jQuery. Live был объявлен устаревшим в версии 1.7.
Майк Барвик
Я также попытался (и я читал это в другом месте - не моя идея) с e.stopPropagation (); e.preventDefault (); и он работает (для меня) почти, но позже я обнаружил, что на самом деле он работает как предсказано 20 раз, но 1 раз нет, так что он не пуленепробиваемый. Смотрите здесь: stackoverflow.com/questions/25671053/… Благодарим вас за комментарии по этому вопросу!
Гаравани
@MikeBarwick Не могли бы вы объяснить свой комментарий или дать ссылку, которая объясняет это? Заранее спасибо.
Биллаль Бегерадж
22

Простое добавление return false;в конце on("click touchstart")функции события может решить эту проблему.

$(this).on("click touchstart", function() {
  // Do things
  return false;
});

Из документации по jQuery .on ()

Возврат falseиз обработчика событий вызовет автоматически event.stopPropagation()и event.preventDefault(). falseЗначение также может быть передано для обработчика в качестве краткого обозначения function(){ return false; }.

Рой
источник
2
отлично работает, срабатывает только один раз на устройствах с обоими - это кажется мне самым чистым наименее хакерским решением
Адам Купер
Лучше всего сработало для меня и имело смысл, почему.
Amir5000 12.12.16
Есть ли минусы к этому? Почему такое простое решение не широко известно?
ESR
10

Я должен был сделать что-то подобное. Вот упрощенная версия того, что сработало для меня. Если событие касания обнаружено, удалите привязку щелчка.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click');

  //your code here
});

В моем случае событие click было привязано к <a>элементу, поэтому мне пришлось удалить привязку click и повторно привязать событие click, что предотвратило действие по умолчанию для <a>элемента.

$thing.on('touchstart click', function(event){
  if (event.type == "touchstart")
    $(this).off('click').on('click', function(e){ e.preventDefault(); });

  //your code here
});
Тим
источник
Хотелось бы, чтобы это работало как есть, но это не так. $ (это) не указывает на то, что вы думаете, что делает.
Дейв Лоурре
8

Мне удалось следующим образом.

Очень просто...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});
Hasanavi
источник
4
Разве это не сработает дважды на устройствах, которые регистрируют как щелчок, так и сенсорный запуск?
DA.
8
Извини, я тебя узнал. Да, вы правы, прикосновение сгенерирует событие touchstart и событие click. Это называется призрачным щелчком. Google реализовал решение для этого. Может не прямо, но работает отлично. Вот ссылка. code.google.com/mobile/articles/fast_buttons.html#ghost
Хасанави,
2
может быть тривиальным, но вы пропустите eпараметр вfunction()
ProbleOfSumit
@Hasanavi это было отличное решение, но я нашел лучшие результаты, используя .on
Нейл
1
Спасибо @Neil. Да, использование .on - лучшая идея, поскольку она может быть удалена в будущей версии. Я изменил свой пример с привязки на.
Хасанави
5

Я считаю, что наилучшей практикой сейчас является использование:

$('#object').on('touchend mouseup', function () { });

touchend

Событие касания запускается, когда точка касания удаляется с поверхности касания.

Событие touchend не вызовет никаких событий мыши.


MouseUp

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

Событие mouseup не вызовет никаких событий касания.

ПРИМЕР

$('#click').on('mouseup', function () { alert('Event detected'); });
$('#touch').on('touchend', function () { alert('Event detected'); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="click">Click me</h1>
<h1 id="touch">Touch me</h1>


РЕДАКТИРОВАТЬ (2017)

С 2017 года браузеры, начиная с Chrome и делающие шаги к тому, чтобы сделать событие щелчка .on("click")более совместимым как для мыши, так и для касания, устраняя задержку, создаваемую событиями касания при запросах на нажатие.

Это приводит к выводу, что возвращение к использованию только события click было бы самым простым решением для продвижения вперед.

Я еще не проводил кросс-браузерное тестирование, чтобы увидеть, насколько это практично.

DreamTeK
источник
Это показалось мне многообещающим, поэтому я немного повозился с ним, но в итоге я обнаружил mouseupнепредсказуемые результаты, особенно при использовании трекпада вместо традиционной мыши.
Skybondsor
4

Как правило, вы не хотите смешивать API по умолчанию для сенсорного и не сенсорного (щелчка). Перейдя в сенсорный мир, вам будет проще работать только с сенсорными функциями. Ниже приведен псевдокод, который будет делать то, что вы хотите.

Если вы подключаетесь к событию touchmove и отслеживаете местоположения, вы можете добавить дополнительные элементы в функцию doTouchLogic для обнаружения жестов и тому подобного.

var touchStartTime;
var touchStartLocation;
var touchEndTime;
var touchEndLocation;

$thing.bind('touchstart'), function() {
     var d = new Date();
     touchStartTime = d.getTime();
     touchStartLocation = mouse.location(x,y);
});

$thing.bind('touchend'), function() {
     var d = new Date();
     touchEndTime= d.getTime();
     touchEndLocation= mouse.location(x,y);
     doTouchLogic();
});

function doTouchLogic() {
     var distance = touchEndLocation - touchStartLocation;
     var duration = touchEndTime - touchStartTime;

     if (duration <= 100ms && distance <= 10px) {
          // Person tapped their finger (do click/tap stuff here)
     }
     if (duration > 100ms && distance <= 10px) {
          // Person pressed their finger (not a quick tap)
     }
     if (duration <= 100ms && distance > 10px) {
          // Person flicked their finger
     }
     if (duration > 100ms && distance > 10px) {
          // Person dragged their finger
     }
}
Justin808
источник
Я полагаю, в этом суть вопроса: имеет ли смысл пытаться поддерживать обе модели с одной базой кода. Я обновлю свой вопрос еще несколькими сценариями.
DA.
1
«Как правило, вы не хотите смешивать сенсорный и не сенсорный по умолчанию», пройдя через это, я согласен. Проблема в том, что они продолжают создавать сенсорные устройства с клавиатурой, что является головной болью для нас, разработчиков.
DA.
Я согласен, что было бы намного легче сделать вначале окончательное решение касания JS или не касаться. Какой бы это был легкий мир! Но что это за эти проклятые устройства, чувствительные к щелчкам и прикосновениям? Стоит ли иметь все эти головные боли из-за них? Это будущее я спрашиваю себя?
Гаравани
Мне не нравится идея иметь несколько определений функций. Моя компания в настоящее время работает над проектом, в котором iPad специально не рассматривается как мобильное устройство, и вам все еще нужно событие touchend. Но touchend не работает на обычных настольных версиях. Таким образом, решение @mottie абсолютно подходит для этого сценария.
Пит
3

Ну ... Все это очень сложно.

Если у вас есть Modernizr, это не просто.

ev = Modernizr.touch ? 'touchstart' : 'click';

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});
harelpls
источник
2
Можете ли вы объяснить, что это делает? Я считаю, что он проверяет, поддерживает ли устройство касание, а если нет, использует щелчок. Загвоздка (или , по крайней мере , было , когда я писал этот вопрос) в том , что некоторые устройства поддерживают оба. И на этих устройствах мне все еще нужно обрабатывать оба события, так как триггер может исходить либо от прикосновения, либо от щелчка.
DA.
2
Я рекомендую немного больше объяснений.
Аарон Холл
2

Еще одна реализация для лучшего обслуживания. Тем не менее, этот метод также будет делать event.stopPropagation (). Клик не фиксируется ни на одном другом элементе, который кликал в течение 100 мс.

var clickObject = {
    flag: false,
    isAlreadyClicked: function () {
        var wasClicked = clickObject.flag;
        clickObject.flag = true;
        setTimeout(function () { clickObject.flag = false; }, 100);
        return wasClicked;
    }
};

$("#myButton").bind("click touchstart", function (event) {
   if (!clickObject.isAlreadyClicked()) {
      ...
   }
}
Матье Хамел
источник
2

Просто для целей документирования, вот что я сделал для самого быстрого / самого отзывчивого клика на рабочем столе / касания мобильного решения, о котором я мог подумать:

Я заменил onфункцию jQuery на модифицированную, которая всякий раз, когда браузер поддерживает сенсорные события, заменяла все мои события клика сенсорным запуском.

$.fn.extend({ _on: (function(){ return $.fn.on; })() });
$.fn.extend({
    on: (function(){
        var isTouchSupported = 'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
        return function( types, selector, data, fn, one ) {
            if (typeof types == 'string' && isTouchSupported && !(types.match(/touch/gi))) types = types.replace(/click/gi, 'touchstart');
            return this._on( types, selector, data, fn);
        };
    }()),
});

Использование чем было бы точно так же, как и раньше, например:

$('#my-button').on('click', function(){ /* ... */ });

Но он будет использовать сенсорный запуск, когда доступно, нажмите, когда нет. Никаких задержек не требуется: D

Жерсон Гуларт
источник
1
Преимущество этого - устройства, поддерживающие ОБА сенсорную и ИСКЛЮЧИТЕЛЬНУЮ клавиатуры, в которых вам необходимо убедиться, что вы используете оба взаимодействия.
DA.
Хорошее наблюдение @DA. Я добавил проверку, чтобы заменить событие нажатия, только если вы не используете какое-либо событие касания в сочетании с ним. Стиль не будет решением для каждого проекта, но я уверен, что он подойдет многим.
Жерсон Гуларт
2

Мне просто пришла в голову идея запоминать, если ontouchstartкогда-либо срабатывал. В этом случае мы находимся на устройстве, которое поддерживает его и хотим игнорировать onclickсобытие. Так как ontouchstartвсегда должен запускаться раньше onclick , я использую это:

<script> touchAvailable = false; </script>
<button ontouchstart="touchAvailable=true; myFunction();" onclick="if(!touchAvailable) myFunction();">Button</button>

jakob.j
источник
1
Поскольку пользователи могут использовать касание и мышь во время одного и того же посещения страницы, проблема этого фрагмента заключается в том, что он регистрирует флаг touchAvailable один раз, а не для каждого события. Плагин jQuery от stackoverflow.com/a/26145343/328272 делает то же самое, что и ваш код, но может указать для события мыши, было ли оно вызвано касанием или мышью на основе события. Не только т.е. по щелчку, но также по отпуску мыши, который может быть вызван секундами (или минутами) после ввода мыши (идеально подходит для всплывающих меню, которые должны расширяться при наведении курсора мышью с помощью жестов мыши или щелчка при использовании касания).
lmeurs
2

Вы можете попробовать вот так:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);
Sky Yip
источник
А как насчет устройств, поддерживающих сенсорное управление и мышь
Walle Cyril
2

В моем случае это работало отлично:

jQuery(document).on('mouseup keydown touchend', function (event) {
var eventType = event.type;
if (eventType == 'touchend') {
    jQuery(this).off('mouseup');
}
});

Основная проблема заключалась в том, что вместо того, чтобы использовать мышку, я попытался щелкнуть, на сенсорных устройствах одновременно сработали щелчок и касание. Если я использую щелчок, некоторые функции на мобильных устройствах вообще не работают. Проблема с щелчком состоит в том, что это глобальное событие, которое запускает остальную часть события, включая touchend.

Пепита Рондерос
источник
1

Это сработало для меня, мобильный телефон слушает оба, так что предотвратите тот, который является сенсорным событием. Рабочий стол только слушать мышь.

 $btnUp.bind('touchstart mousedown',function(e){
     e.preventDefault();

     if (e.type === 'touchstart') {
         return;
     }

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });
Леварне Соботкер
источник
1

Будучи для меня лучшим ответом, который дал Мотти, я просто пытаюсь сделать его код более пригодным для повторного использования, так что это мой вклад:

bindBtn ("#loginbutton",loginAction);

function bindBtn(element,action){

var flag = false;
$(element).bind('touchstart click', function(e) {
    e.preventDefault();
    if (!flag) {
        flag = true;
        setTimeout(function() {
            flag = false;
        }, 100);
        // do something
        action();
    }
    return false;
});
Arco Voltaico
источник
0

Я также работаю над веб-приложением для Android / iPad, и кажется, что для «перемещения компонентов» достаточно использовать только «touchmove» (нет необходимости в touchstart). Отключив сенсорный запуск, вы можете использовать .click (); из jQuery. Это на самом деле работает, потому что он не был перегружен Touchstart.

Наконец, вы можете binb .live ("touchstart", function (e) {e.stopPropagation ();}); чтобы попросить событие touchstart прекратить распространение, нажмите кнопку (), чтобы вызвать событие.

Это сработало для меня.

nembleton
источник
Как бы вы отключили touchstart?
punkrockbuddyholly
Я считаю, что вы "могли бы" сделать что-то вроде: jQuery ("# ​​my_element"). On ('touchstart', function (e) {e.preventDefault ()});
Нэмблтон
0

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

Полное решение см. На странице https://developers.google.com/mobile/articles/fast_buttons.

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

SystemParadox
источник
0

Может быть эффективно назначать событиям 'touchstart mousedown'или 'touchend mouseup'избегать нежелательных побочных эффектов использования click.

Стивен Лу
источник
0

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

$('#buttonId').on('touchstart click', function(event){
    if ($(this).data("already")) {
        $(this).data("already", false);
        return false;
    } else if (event.type == "touchstart") {
        $(this).data("already", true);
    }
    //your code here
});

По сути, всякий раз, когда ontouchstartсобытие срабатывает на элемент, флаг устанавливает и затем впоследствии удаляется (и игнорируется), когда происходит щелчок.

BigMacAttack
источник
0

Почему бы не использовать jQuery Event API?

http://learn.jquery.com/events/event-extensions/

Я использовал это простое событие с успехом. Он чистый, пространственный и достаточно гибкий, чтобы его можно было улучшить.

var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);
var eventType = isMobile ? "touchstart" : "click";

jQuery.event.special.touchclick = {
  bindType: eventType,
  delegateType: eventType
};
mdbiscan
источник
1
Рекомендуется использовать функцию обнаружения по анализу пользовательского агента.
Джинглстхула
Подвох - это устройства, которые поддерживают как прикосновение, так и щелчок.
DA.
0

Если вы используете jQuery, для меня это сработало:

var callback; // Initialize this to the function which needs to be called

$(target).on("click touchstart", selector, (function (func){
    var timer = 0;
    return function(e){
        if ($.now() - timer < 500) return false;
        timer = $.now();
        func(e);
    }
})(callback));

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

Может помочь кому-то в подобной ситуации!

Юсуф Бхабхравала
источник
0

Для простых функций, просто распознать касание или нажмите Я использую следующий код:

var element = $("#element");

element.click(function(e)
{
  if(e.target.ontouchstart !== undefined)
  {
    console.log( "touch" );
    return;
  }
  console.log( "no touch" );
});

Это вернет «touch», если определено событие touchstart, и «no touch», если нет. Как я уже сказал, это простой подход для событий щелчка / касания.

Родриго
источник
0

Я пытаюсь это, и пока это работает (но я только на Android / PhoneGap, так что будьте бдительны)

  function filterEvent( ob, ev ) {
      if (ev.type == "touchstart") {
          ob.off('click').on('click', function(e){ e.preventDefault(); });
      }
  }
  $('#keypad').on('touchstart click', '.number, .dot', function(event) {
      filterEvent( $('#keypad'), event );
      console.log( event.type );  // debugging only
           ... finish handling touch events...
  }

Мне не нравится тот факт, что я перепривязываю обработчики при каждом касании, но все рассматриваемые прикосновения происходят не очень часто (в компьютерное время!)

У меня есть МНОЖЕСТВО обработчиков, подобных тому, что используется для «#keypad», поэтому, имея такую ​​простую функцию, которая позволяет мне справиться с проблемой без лишнего кода, я пошел по этому пути.

Дейв Ловере
источник
0

РЕДАКТИРОВАТЬ: Мой прежний ответ (на основе ответов в этой теме) не был путь для меня. Я хотел, чтобы подменю расширялось при щелчке мышью при вводе или касании и сворачивалось при отпускании или щелчке мышью. Поскольку события мыши обычно запускаются после событий касания, было довольно сложно написать обработчики событий, которые одновременно поддерживают ввод данных с сенсорного экрана и мыши.

Плагин jQuery: Touch Or Mouse

Я закончил тем, что написал плагин jQuery под названием « Touch Or Mouse » (минимизировано 897 байт), который может определять, было ли событие вызвано сенсорным экраном или мышью (без проверки поддержки сенсорного ввода!). Это позволяет поддержку как сенсорный экран и мышь одновременно и полностью отделить свои события.

Таким образом, OP может использовать touchstartили touchendдля быстрого реагирования на сенсорные нажатия и clickдля кликов, вызываемых только мышью.

демонстрация

Сначала нужно сделать то есть. элементы тела отслеживают события касания:

$(document.body).touchOrMouse('init');

Мышиные события связаны с элементами по умолчанию, и с помощью вызова $body.touchOrMouse('get', e)мы можем узнать, было ли событие вызвано сенсорным экраном или мышью.

$('.link').click(function(e) {
  var touchOrMouse = $(document.body).touchOrMouse('get', e);

  if (touchOrMouse === 'touch') {
    // Handle touch click.
  }
  else if (touchOrMouse === 'mouse') {
    // Handle mouse click.
  }
}

Смотрите плагин на работе на http://jsfiddle.net/lmeurs/uo4069nh .

объяснение

  1. Этот плагин должен быть вызван на т.е. элемент body для отслеживания touchstartи touchendсобытий, таким образом, touchendсобытие не должно запускаться по элементу триггера (то есть по ссылке или кнопке). Между этими двумя событиями касания этот плагин считает, что любое событие мыши вызывается касанием.
  2. События мыши срабатывают только после того touchend, как , когда событие мыши ghostEventDelayсработало в (опция, 1000 мс по умолчанию) после touchend, этот плагин считает, что событие мыши вызывается прикосновением.
  3. При нажатии на элемент с помощью сенсорного экрана элемент получает :activeсостояние. mouseleaveСобытие вызывается только после того , как элемент теряет это состояние , т.е.. нажав на другой элемент. Поскольку это может быть через несколько секунд (или минут!) После того, как mouseenterсобытие было запущено, этот плагин отслеживает последнее mouseenterсобытие элемента : если последнее mouseenterсобытие было вызвано касанием, следующее mouseleaveсобытие также считается вызванным касанием.
lmeurs
источник
0

Вот простой способ сделать это:

// A very simple fast click implementation
$thing.on('click touchstart', function(e) {
  if (!$(document).data('trigger')) $(document).data('trigger', e.type);
  if (e.type===$(document).data('trigger')) {
    // Do your stuff here
  }
});

Вы в основном сохраняете первый тип события, которое запускается, в свойство 'trigger' в объекте данных jQuery, который присоединен к корневому документу, и выполняете только тогда, когда тип события равен значению в 'trigger'. На сенсорных устройствах цепочка событий, скорее всего, будет «touchstart» с последующим «щелчком»; однако обработчик «щелчка» не будет выполнен, потому что «щелчок» не соответствует исходному типу события, сохраненному в «триггере» («сенсорный запуск»).

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

Вот кодекс, с которым вы можете поиграть (попробуйте нажать на кнопку на сенсорном устройстве - не должно быть задержки нажатия): http://codepen.io/thdoan/pen/xVVrOZ

Я также реализовал это как простой плагин jQuery, который также поддерживает фильтрацию потомков jQuery путем передачи строки селектора:

// A very simple fast click plugin
// Syntax: .fastClick([selector,] handler)
$.fn.fastClick = function(arg1, arg2) {
  var selector, handler;
  switch (typeof arg1) {
    case 'function':
      selector = null;
      handler = arg1;
      break;
    case 'string':
      selector = arg1;
      if (typeof arg2==='function') handler = arg2;
      else return;
      break;
    default:
      return;
  }
  this.on('click touchstart', selector, function(e) {
    if (!$(document).data('trigger')) $(document).data('trigger', e.type);
    if (e.type===$(document).data('trigger')) handler.apply(this, arguments);
  });
};

Codepen: http://codepen.io/thdoan/pen/GZrBdo/

thdoan
источник
Почему вы использовали свойство данных в документе, а не просто переменную?
Рафаэль Швейкерт
@RaphaelSchweikert Вероятно, просто по привычке. Там нет правильного или неправильного пути, но я хотел бы использовать $.data()для хранения данных, которые могут быть доступны для нескольких функций, но не относятся к глобальной области. Плюс в том, что, поскольку я храню его в элементе, он, естественно, предоставляет мне больше контекста.
thdoan
0

Лучший метод, который я нашел, это написать событие касания и заставить это событие вызывать событие обычного щелчка программно. Таким образом, у вас есть все ваши обычные события нажатия, а затем вам нужно добавить только один обработчик событий для всех событий касания. Для каждого узла, который вы хотите сделать сенсорным, просто добавьте в него класс «сенсорный», чтобы вызвать сенсорный обработчик. С Jquery это работает так с некоторой логикой, чтобы удостовериться, что это настоящее событие касания, а не ложное срабатывание.

$("body").on("touchstart", ".touchable", function() { //make touchable  items fire like a click event
var d1 = new Date();
var n1 = d1.getTime();
setTimeout(function() {
    $(".touchable").on("touchend", function(event) {
        var d2 = new Date();
        var n2 = d2.getTime();
        if (n2 - n1 <= 300) {
            $(event.target).trigger("click"); //dont do the action here just call real click handler
        }
    });
}, 50)}).on("click", "#myelement", function() {
//all the behavior i originally wanted
});
mahatrsna
источник