Як зв’язати події "touchstart" і "click", але не відповідати на обидва?


198

Я працюю на мобільному веб-сайті, який має працювати на різних пристроях. На даний момент у мене болить голова - це BlackBerry.

Нам потрібно підтримувати як натискання клавіатури, так і сенсорні події.

В ідеалі я б просто використовував:

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

але проблема, з якою ми стикаємося, полягає в тому, що деякі з цих пристроїв ожини мають дуже дратівливу затримку з моменту дотику до неї, що викликає клацання.

Засіб полягає в тому, щоб замість цього використовувати сенсорний запуск:

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

Але як я можу прив’язати обидві події, а лише розпалити одну? Мені все ще потрібна подія клацання для клавіатурних пристроїв, але, звичайно, не хочу запускати події клацання, якщо я використовую сенсорний пристрій.

Бонусне запитання: чи все-таки це робити і додатково розміщувати браузери, які навіть не мають сенсорного початку? Досліджуючи це, схоже, що BlackBerry OS5 не підтримує сенсорний запуск, тому також доведеться покладатися на події клацання для цього браузера.

ДОДАТИ:

Можливо, більш вичерпне питання:

Чи можна за допомогою jQuery обробляти як сенсорні, так і взаємодії миші з однаковими прив'язками?

В ідеалі відповідь - так. Якщо ні, у мене є кілька варіантів:

1) Ми використовуємо WURFL для отримання інформації про пристрій, щоб створити власну матрицю пристроїв. Залежно від пристрою, ми будемо використовувати сенсорний АБО клацання.

2) Визначте підтримку дотику в браузері через JS (мені потрібно ще кілька досліджень щодо цього, але здається, що це можливо).

Однак це все ще залишає одне питання: як щодо пристроїв, які підтримують BOTH. Деякі телефони, які ми підтримуємо (а саме Nokias та BlackBerries), мають як сенсорні екрани, так і клавіатури. Тож такий спосіб повертає мене до повного кола до початкового питання ... чи є спосіб якось дозволити обом відразу?


2
Вам краще прив’язатись до сенсорного запуску та дотику та записати власну логіку клацання поряд з логікою дотику. Вбудований зворотний дзвінок як відсутність знань про дотики.
Justin808

Я не впевнений, що я слідкую за цим, Джастін. Чи не було б у мене до цього часу пов’язана подія сенсорного запуску та натискання?
DA.

@DA - ні, ви взагалі не прив'язуєтесь до зворотного дзвінка .click (). Я спробую написати відповідь у якийсь код судо. У мене немає сенсорного пристрою, зручного для написання реального коду :)
Justin808

Ах, але для уточнення мені все ж потрібні події клацання, оскільки люди, які відвідують цей сайт, мають нетипові пристрої.
DA.

2
Використання .bind('touchstart mouseup')вирішить це (на основі одного з коментарів нижче)
oriadam

Відповіді:


135

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


Прив’яжіть до обох, але зробіть прапор, щоб функція запускалася лише один раз на 100 мс або близько того.

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

  return false
});

7
хм ... це здається хитким, але це може спрацювати. Проблема полягає в тому, що на деяких із цих пристроїв це помітне відставання ... можливо, майже секунду ..., що буде дратувати інших на більш швидкому пристрої.
DA.

15
Замість того щоб використовувати clickзміна його mousedown... може бути тривала затримка різниця між mousedownі mouseupяка , як clickвизначається.
Мотті

7
Це рішення, з яким я пішов. Дякую! Що я роблю, це те, що я позначаю елемент touchend, застосовуючи клас touched. Це запускається перед викликом події клацання. Потім я додаю подію клацання, яка спочатку перевіряє наявність цього класу. Якщо він є, ми припускаємо, що сенсорні події запускаються, і ми нічого не робимо за допомогою клацання, крім видалення назви класу, щоб «скинути» його для наступної взаємодії. Якщо класу немає, то ми припускаємо, що вони використовували клавіатуру, щоб клацнути елемент і запустити функцію звідти.
DA.

7
Ви повинні знати, що у більшості браузерів телефонів затримка 300ms на події натискання, тож слід збільшити час затримки як мінімум до 300, Дивіться цю статтю щодо проблеми затримки 300ms . Це в основному для того, щоб увімкнути функцію "подвійного клацання для збільшення", що було дуже важливою особливістю в перші дні iPhone, коли більшість веб-сайтів були розроблені для перегляду на великому екрані робочого столу.
трепет

12
На даний момент це питання було розглянуто майже в 100 тисяч разів. Це виглядає як надзвичайно поширений випадок / проблема використання. Однак ця відповідь та інші на це та подібні запитання здаються химерними. Рішення 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кожен раз, коли подія запускається. Ось рішення: встановіть прапор "обробляється" в самому цільовому елементі за допомогою jQuery.data().
thoan

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

5
Я думаю, що проблема полягає в тому, що це тестування можливостей пристрою, а не те, що робить користувач, правда? Завдання полягає в тому, що нам потрібно підтримувати сенсорні пристрої, які також мають клавіатури (щоб вони могли використовувати обидва)
DA.

8
Це не дуже гарна ідея - зламає подію для користувача з сенсорним екраном і мишкою при використанні миші (ноутбуки з сенсорними екранами стають досить поширеними).
Крістіан Дж.

1
Відмінна стаття, пов’язана з цим: hacks.mozilla.org/2013/04/…
Лука Мелія

У Windows 8 завжди буде передаватися подія "touchstart", якщо це використовується, оскільки "ontouchstart" поверне "true" для цієї ОС. Тож, мабуть, не найкраща відповідь, якщо ваш код працюватиме лише на справжніх сенсорних екранах, але тоді, якщо це так, перевірка вам взагалі не потрібна.
Ніл Монро

На ноутбуках із сенсорним екраном користувач не зможе натиснути що-небудь, використовуючи цей код у хромі (принаймні, chrome 46). IE 11, здається, працює.
Девід Шеррет

42

Зазвичай це також працює:

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

});

8
@Jonathan помиляється, якщо ви використовуєте новіші версії jQuery. У режимі "Живий" застаріла версія 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

Якісь мінуси до цього? Чому таке просте рішення не відоме широко?
ESR

10

Мені довелося зробити щось подібне. Ось спрощена версія того, що працювало для мене. Якщо виявлена ​​подія дотику, видаліть прив'язку кліків.

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

  //your code here
});

У моєму випадку подія клацання була прив’язана до <a>елемента, тому мені довелося видалити прив'язку клацання та повторно пов’язати подію натискання, що перешкоджало дії за замовчуванням для <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
});

4
чи не буде одразу двічі на пристроях, які реєструють як клацання, так і сенсорний запуск?
DA.

8
Вибачте, я вас зрозумів. Так, ви маєте рацію, що дотик також призведе до події сенсорного запуску та події клацання. Це називається клацанням привидів. Google реалізував рішення для цього. Можливо, не прямо, але працює чудово. Ось посилання. code.google.com/mobile/articles/fast_buttons.html#ghost
Hasanavi

2
може бути тривіальним, але вам не вистачає eпараметра вfunction()
ProblemsOfSumit

@Hasanavi це було чудовим виправленням, проте я знайшов кращі результати, використовуючи .on
Ніл

1
Дякую @Neil. Так, використання .on - це краща ідея, оскільки вона може бути видалена в майбутній версії. Я змінив свій приклад із прив’язки до на.
Хасанаві

5

Я вважаю, що зараз найкращою практикою є використання:

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

дотик

Подія дотику запускається, коли з дотикової поверхні виймається точка дотику.

Подія touchend не спричинить жодних подій миші.


миша

Подія миші надсилається елементу, коли вказівник миші над елементом, і кнопка миші відпускається. Будь-який елемент HTML може отримати цю подію.

Подія миші не спричинить жодних сенсорних подій.

ПРИКЛАД

$('#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>


EDIT (2017)

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

Це призводить до висновку, що повернення до використання події просто клацання було б найпростішим рішенням, яке рухається вперед.

Я ще не робив жодного перехресного тестування браузера, щоб перевірити, чи це практично.


Це здалося мені багатообіцяючим, тому я заплутався з ним купою, але в кінцевому підсумку виявив, 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
     }
}

Я вважаю, що це суть питання: чи має сенс намагатися підтримувати обидві моделі з однією базою коду. Я оновлю своє запитання ще кількома сценаріями.
DA.

1
"Як правило, ви не хочете змішувати дотик за замовчуванням і без дотику" після цього, я згоден. Проблема полягає в тому, що вони продовжують робити сенсорні пристрої за допомогою клавіатур, що є головним болем для нас, розробників.
DA.

Я погоджуюся, що було б набагато простіше зробити на початку остаточне рішення js або без дотику. Який легкий світ був би! Але що це за ці прокляті пристрої, чутливі до натискання та дотику? Чи варто мати через них всі ті головні болі? Це майбутнє, про яке я запитую себе?
Гаравани

Мені не подобається ідея мати декілька визначень функцій. В даний час моя компанія працює над проектом, коли iPad не використовується як мобільний пристрій, і ви все ще потребуєте події на дотик. Але touchend не працює у звичайних версіях для настільних ПК. Тож рішення @mottie є абсолютним штрафом для цього сценарію.
Піт


3

Ну ... Все це дуже складно.

Якщо у вас є модернізатор, це не маніпулятор.

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

$('#menu').on(ev, '[href="#open-menu"]', function(){
  //winning
});

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. Я додав чек, щоб замінити подію натискання лише тоді, коли ви не використовуєте жодної сенсорної події в поєднанні з нею. Stil не буде рішенням для кожного проекту, але я впевнений, що він підходить для багатьох.
Герсон Гуларт

2

Я щойно придумав ідею запам’ятати, якщо ontouchstartвона коли-небудь запускалася. У цьому випадку ми знаходимось на пристрої, який його підтримує, і хочемо ігнорувати onclickподію. Оскільки ontouchstartзавжди слід спрацьовувати раніше onclick , я використовую це:

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


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

2

Ви можете спробувати так:

var clickEvent = (('ontouchstart' in document.documentElement)?'touchstart':'click');
$("#mylink").on(clickEvent, myClickHandler);

як щодо пристроїв, які підтримують сенсорне та
мишеве

2

У моєму випадку це спрацювало чудово:

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

Основна проблема полягала в тому, що замість миші я намагався натиснути, на сенсорних пристроях викликали натискання та торкання одночасно, якщо я використовую клавішу, деяка функціональність взагалі не працює на мобільних пристроях. Проблема з клацанням полягає в тому, що це глобальна подія, яка запускає решту події, включаючи дотик.


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

0

Я також працюю над веб-додатком для Android / iPad, і, здається, достатньо лише "touchmove" для "переміщення компонентів" (не потрібно сенсорного запуску). Відключивши сенсорний запуск, ви можете використовувати .click (); від jQuery. Це насправді працює, тому що його не перевантажено сенсорним стартом.

Нарешті, ви можете binb .live ("touchstart", функція (e) {e.stopPropagation ();}); попросити подію сенсорного запуску припинити поширювати, вітальня натиснути (), щоб спрацьовувати.

Це працювало для мене.


Як би ви відключили touchstart?
punkrockbuddyholly

Я вважаю, що ви "могли" зробити щось на кшталт: jQuery ("# ​​my_element"). On ('touchstart', function (e) {e.preventDefault ()});
Немблтон

0

Є багато речей, які слід враховувати, намагаючись вирішити це питання. Більшість рішень або порушують прокрутку, або не обробляють належним чином події клацання.

Повне рішення див веб-сторінці https://developers.google.com/mobile/articles/fast_buttons

Примітка: Ви не можете обробляти події клацання привидів на основі елемента. Затримка натискання запускається за місцем екрана, тому якщо ваші сенсорні події модифікують сторінку певним чином, подія натискання буде надіслана до нової версії сторінки.


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подія запускає елемент, позначає набір і потім згодом видаляється (і ігнорується) при натисканні клацання.


0

Чому б не використовувати API jQuery Event?

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

1
Рекомендується використовувати функцію виявлення функцій над нюхом агента користувача.
jinglesthula

Захоплення - це пристрої, які підтримують як натискання, так і клацання.
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...
  }

Мені не подобається те, що я повторно зв'язую оброблювачів на кожному дотику, але всі речі, які вважаються дотиками, трапляються не дуже часто (у комп’ютерний час!)

У мене є TON обробників, таких як "#keypad", тому маючи просту функцію, яка дозволяє мені вирішувати проблему без зайвого коду, тому я пішов цим шляхом.



0

EDIT: Моя колишня відповідь (заснована на відповідях у цій темі) не була для мене дорогою. Я хотів, щоб підменю розгорнулося при введенні миші або натисканні миші та згорнулось у відпустці миші або іншому натисканні клацання. Оскільки події миші зазвичай знімаються після сенсорних подій, писати слухачів подій, які підтримують і сенсорний екран, і введення миші, було дуже складно.

плагін jQuery: торкніться або миші

Я закінчив писати плагін jQuery під назвою " Touch Or Mouse " (897 байт мінімізований), який може визначити, чи викликала подію сенсорний екран або миша (без тестування на підтримку сенсорного режиму!). Це дозволяє підтримку як сенсорний екран і миша одночасно і повністю відокремити свої події.

Таким чином ОП може використовувати 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. Цей плагін потрібно викликати, тобто. елемент тіла для відстеження touchstartта touchendподій, таким чином touchendподія не повинна бути запущена на спусковий елемент (тобто посилання або кнопку). Між цими двома подіями дотику цей плагін вважає, що будь-яка подія миші викликається дотиком.
  2. Події миші запускаються лише після того touchend, як після запуску події миші в межах ghostEventDelay(опція, 1000 мс за замовчуванням) після touchendцього плагіна вважає, що подія миші викликається дотиком.
  3. При натисканні на елемент за допомогою сенсорного екрана елемент набуває :activeстану. mouseleaveПодія викликається тільки після того , як елемент втрачає цей стан , тобто .. натискання на інший елемент. Оскільки це може бути через секунди (або хвилини!) Після mouseenterзапуску події, цей плагін відстежує останню mouseenterподію елемента : якщо остання mouseenterподія була викликана дотиком, наступна mouseleaveподія також вважається викликаною дотиком.

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

Ви в основному зберігаєте перший тип події, який викликається властивістю "тригер" в об'єкті даних jQuery, який прикріплений до кореневого документа, і виконується лише тоді, коли тип події дорівнює значенню в "тригері". На пристроях, що торкаються, ланцюжок подій, ймовірно, буде "сенсорним", а потім "клацанням"; однак обробник "натискання" не буде виконаний, оскільки "натискання" не відповідає початковому типу події, збереженому в "тригері" ("сенсорний запуск").

Припущення, і я вважаю, що це безпечно, полягає в тому, що ваш смартфон не буде мимовільно змінюватися з сенсорного пристрою на пристрій миші, інакше дотик ніколи не зареєструється, оскільки тип події "тригер" зберігається лише один раз на завантаження сторінки та "натискання" ніколи не збігаються з "сенсорним".

Ось коден, з яким можна пограти (спробуйте натиснути кнопку на сенсорному пристрої - не повинно бути затримок натискання): 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/


Чому ви використовували властивість даних у документі, а не просто використовували змінну?
Рафаель Швайкерт

@RaphaelSchweikert Напевно, просто за звичкою. Немає правильного чи неправильного способу, але я люблю використовувати $.data()для зберігання даних, доступ до яких можна отримати за допомогою декількох функцій, але не належать до глобальної сфери. Плюсом є те, що оскільки я зберігаю його в елементі, він, природно, забезпечує мені більше контексту.
thoan

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
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.