webentwicklung-frage-antwort-db.com.de

Wie binde ich Touchstart- und Klickereignisse, aber reagiere nicht auf beide?

Ich arbeite an einer mobilen Website, die auf einer Vielzahl von Geräten funktionieren muss. Die, die mir momentan Kopfschmerzen bereiten, sind Blackberry.

Wir müssen sowohl Tastaturklicks als auch Berührungsereignisse unterstützen.

Im Idealfall würde ich nur verwenden:

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

aber das Problem, auf das wir stoßen, ist, dass einige dieser BlackBerry-Geräte eine sehr ärgerliche Verzögerung vom Zeitpunkt der Berührung bis zum Auslösen eines Klicks aufweisen.

Das Mittel ist, stattdessen Touchstart zu verwenden:

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

Aber wie gehe ich vor, um beide Ereignisse zu binden, aber nur eines auszulösen? Ich benötige das Click-Ereignis weiterhin für Tastaturgeräte, möchte aber natürlich nicht, dass das Click-Ereignis ausgelöst wird, wenn ich ein Touch-Gerät verwende.

Eine Bonusfrage: Gibt es überhaupt eine Möglichkeit, dies zu tun und zusätzlich Browser zu unterstützen, die nicht einmal ein Touchstart-Ereignis haben? Nachforschungen haben ergeben, dass BlackBerry OS5 Touchstart nicht unterstützt. Sie müssen daher auch auf Klickereignisse für diesen Browser angewiesen sein.

NACHTRAG:

Vielleicht ist eine umfassendere Frage:

Ist es mit jQuery möglich/empfehlenswert, sowohl Touch-Interaktionen als auch Maus-Interaktionen mit denselben Bindungen abzuwickeln?

Idealerweise lautet die Antwort ja. Wenn nicht, habe ich einige Möglichkeiten:

1) Wir verwenden WURFL, um Geräteinformationen abzurufen, sodass wir eine eigene Matrix von Geräten erstellen können. Je nach Gerät verwenden wir touchstart OR click.

2) Erkennen für Touch-Unterstützung im Browser über JS (Ich muss etwas mehr darüber recherchieren, aber es scheint, dass dies machbar ist).

Dennoch bleibt ein Problem: Was ist mit Geräten, die BEIDE unterstützen? Einige der von uns unterstützten Telefone (nämlich das Nokia und das BlackBerry) haben beide Touchscreens nd Tastaturen. So schließt sich der Kreis zur ursprünglichen Frage ... gibt es eine Möglichkeit, beides gleichzeitig zuzulassen?

188
DA.

Update : Schauen Sie sich das Projekt jQuery Pointer Events Polyfill an, mit dem Sie sich an "Zeiger" -Ereignisse binden können, anstatt zwischen Maus und Maus zu wählen & berühren.


Binden Sie an beide, aber erstellen Sie ein Flag, damit die Funktion nur einmal pro 100 ms oder so ausgelöst wird.

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

  return false
});
130
Mottie

Dies ist das Update, das ich "erstelle" und das den GhostClick entfernt und den FastClick implementiert. Probieren Sie es aus und lassen Sie uns wissen, ob es bei Ihnen funktioniert hat.

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

        // Do your magic here

});
72
Rafael Fragoso

Sie könnten so etwas ausprobieren:

var clickEventType=((document.ontouchstart!==null)?'click':'touchstart');
$("#mylink").bind(clickEventType, myClickHandler);
49
swooter

Normalerweise funktioniert das auch:

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

});
42
Jonathan

Durch Hinzufügen von return false; Am Ende der Ereignisfunktion on("click touchstart") kann dieses Problem behoben werden.

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

Aus der jQuery-Dokumentation zu . On ()

Wenn Sie false von einem Event-Handler zurückgeben, werden event.stopPropagation() und event.preventDefault() automatisch aufgerufen. Ein false -Wert kann auch als Kurzform für function(){ return false; } an den Handler übergeben werden.

18
Roy

Ich musste etwas Ähnliches tun. Hier ist eine vereinfachte Version dessen, was für mich funktioniert hat. Wenn ein Berührungsereignis erkannt wird, entfernen Sie die Klickbindung.

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

  //your code here
});

In meinem Fall war das Klickereignis an ein <a> - Element gebunden, sodass ich die Klickbindung entfernen und ein Klickereignis erneut binden musste, das die Standardaktion für das <a> - Element verhinderte.

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

  //your code here
});
10
Tim

Das ist mir auf folgendem Weg gelungen.

Kinderleicht...

$(this).on('touchstart click', function(e){
  e.preventDefault();
  //do your stuff here
});
7
Hasanavi

Im Allgemeinen möchten Sie die Standard-Touch- und die Nicht-Touch- (Klick-) API nicht mischen. Sobald Sie in die Welt der Berührung eingetreten sind, ist es einfacher, nur mit den berührungsbezogenen Funktionen umzugehen. Nachfolgend finden Sie einen Pseudocode, der genau das tut, was Sie möchten.

Wenn Sie im touchmove-Ereignis eine Verbindung herstellen und die Standorte verfolgen, können Sie der doTouchLogic-Funktion weitere Elemente hinzufügen, um Gesten und so weiter zu erkennen.

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
     }
}
4
Justin808

Ich glaube, die beste Praxis ist jetzt zu verwenden:

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

touchend

Das Berührungsereignis wird ausgelöst, wenn ein Berührungspunkt von der Berührungsoberfläche entfernt wird.

Das Touchend-Ereignis wird nicht Mausereignisse auslösen.


mouseup

Das mouseup-Ereignis wird an ein Element gesendet, wenn sich der Mauszeiger über dem Element befindet und die Maustaste losgelassen wird. Jedes HTML-Element kann dieses Ereignis empfangen.

Das mouseup-Ereignis wird nicht Berührungsereignisse auslösen.

BEISPIEL

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

Ab 2017 beginnen Browser mit Chrome und machen Schritte, um das Klickereignis .on("click") für Maus und Touch kompatibler zu machen, indem die Verzögerung beseitigt wird, die durch Tap-Ereignisse bei Klickanfragen erzeugt wird .

Dies führt zu der Schlussfolgerung, dass es die einfachste Lösung ist, in Zukunft nur noch das Klickereignis zu verwenden.

Ich habe noch keine Cross-Browser-Tests durchgeführt, um festzustellen, ob dies praktisch ist.

4
DreamTeK

Naja ... all das ist super kompliziert.

Wenn Sie modernizr haben, ist es ein Kinderspiel.

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

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

überprüfen Sie die Schnellschaltflächen und Chost-Klicks von google https://developers.google.com/mobile/articles/fast_buttons

3
torbjon

Eine weitere Implementierung für eine bessere Wartung. Diese Technik führt jedoch auch event.stopPropagation () aus. Der Klick wird von keinem anderen Element abgefangen, das 100 ms lang geklickt hat.

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
mathieu hamel

Ich hatte gerade die Idee, mir zu merken, ob ontouchstart jemals ausgelöst wurde. In diesem Fall befinden wir uns auf einem Gerät, das dies unterstützt, und möchten das Ereignis onclick ignorieren. Da ontouchstart immer ausgelöst werden soll vorheronclick, benutze ich folgendes:

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

Du könntest es so versuchen:

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

Nur zu Dokumentationszwecken, hier ist, was ich für die schnellste/reaktionsschnellste Klick-auf-Desktop/Tipp-auf-Mobil-Lösung getan habe, an die ich denken könnte:

Ich habe die Funktion on von jQuery durch eine geänderte ersetzt, die, wenn der Browser Berührungsereignisse unterstützt, alle meine Klickereignisse durch Berührungsstart ersetzt.

$.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);
        };
    }()),
});

Verwendung als wäre genau das gleiche wie zuvor, wie:

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

Aber es würde Touchstart verwenden, wenn verfügbar, klicken Sie, wenn nicht. Keine Verzögerungen jeglicher Art erforderlich: D

2
Gerson Goulart

Das hat bei mir geklappt, Handy hört auf beides, also verhindere das eine, was das Berührungsereignis ist. Desktop nur mit der Maus hören.

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

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

     var val = _step( _options.arrowStep );
               _evt('Button', [val, true]);
  });
1

In meinem Fall hat das perfekt funktioniert:

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

Das Hauptproblem war, als ich stattdessen mouseup mit Klick versuchte, auf Touchgeräten gleichzeitig click und touchend auszulösen. Wenn ich den Klick aus nutze, funktionierten einige Funktionen auf Mobilgeräten überhaupt nicht. Das Problem beim Klicken ist, dass es sich um ein globales Ereignis handelt, das den Rest des Ereignisses einschließlich des berührenden Endes auslöst.

1
Pepita Ronderos

Da ich die beste Antwort auf die Frage von Mottie bin, versuche ich nur, seinen Code wiederverwendbarer zu machen. Das ist also mein Beitrag:

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;
});
1
Arco Voltaico

Ich versuche dies und so weit es funktioniert (aber ich bin nur auf Android/Phonegap, also Vorbehalt Emptor)

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

Ich mag die Tatsache nicht, dass ich Handler bei jeder Berührung neu binde, aber alle Dinge, die als Berührungen betrachtet werden, kommen nicht sehr oft vor (in der Computerzeit!)

Ich habe eine TONNE von Handlern wie das für '#keypad', deshalb habe ich diese Methode gewählt, um das Problem mit einer einfachen Funktion ohne zu viel Code zu lösen.

0
Dave Lowerre

Versuchen Sie, Virtual Mouse (vmouse) Bindings aus jQuery Mobile Zu verwenden. Es ist ein virtuelles Ereignis speziell für Ihren Fall:

$thing.on('vclick', function(event){ ... });

http://api.jquerymobile.com/vclick/

Browser-Unterstützungsliste: http://jquerymobile.com/browser-support/1.4/

0
Sergey Denisov

Ich gab eine Antwort dort und ich demonstriere mit jsfiddle . Sie können nach verschiedenen Geräten suchen und diese melden.

Grundsätzlich benutze ich eine Art Ereignissperre mit einigen Funktionen, die dazu dienen:

/*
 * Event lock functions
 * ====================
 */
function getEventLock(evt, key){
   if(typeof(eventLock[key]) == 'undefined'){
      eventLock[key] = {};
      eventLock[key].primary = evt.type;
      return true;
   }
   if(evt.type == eventLock[key].primary)
      return true;
   else
      return false;
}

function primaryEventLock(evt, key){
   eventLock[key].primary = evt.type;
}

Dann beginne ich in meinen Event-Handlern mit einer Anfrage an meine Sperre:

/*
 * Event handlers
 * ==============
 */
$("#add").on("touchstart mousedown", addStart);
$("#add").on("touchend mouseup", addEnd);
function addStart(evt){
   // race condition between 'mousedown' and 'touchstart'
   if(!getEventLock(evt, 'add'))
      return;

   // some logic
   now = new Date().getTime();
   press = -defaults.pressDelay;
   task();

   // enable event lock and(?) event repetition
   pids.add = setTimeout(closure, defaults.pressDelay);

   function closure(){
        // some logic(?): comment out to disable repetition
      task();

      // set primary input device
      primaryEventLock(evt, 'add');

      // enable event repetition
      pids.add = setTimeout(closure, defaults.pressDelay);
   }
}
function addEnd(evt){
      clearTimeout(pids.add);
}

Ich muss betonen, dass das Problem nicht darin besteht, einfach auf ein Ereignis zu antworten, sondern auf [~ # ~] nicht [~ # ~] auf beide antworten.

Schließlich gibt es unter jsfiddle einen Link zu einer aktualisierten Version, in der ich den vorhandenen Code durch Hinzufügen eines einfachen Aufrufs zu meiner Ereignissperrbibliothek, sowohl bei Ereignisstart- als auch -ende-Handlern, sowie von 2 minimal beeinträchtige Bereichsvariablen eventLock und eventLockDelay.

0
centurian

Die beste Methode, die ich gefunden habe, besteht darin, das Berührungsereignis zu schreiben und dieses Ereignis programmgesteuert das normale Klickereignis aufrufen zu lassen. Auf diese Weise haben Sie alle Ihre normalen Klickereignisse und müssen dann nur einen Ereignishandler für alle Berührungsereignisse hinzufügen. Fügen Sie für jeden Knoten, den Sie berührbar machen möchten, einfach die Klasse "touchable" hinzu, um den Touch-Handler aufzurufen. Bei Jquery funktioniert dies mit einer gewissen Logik, um sicherzustellen, dass es sich um ein echtes Berührungsereignis und nicht um ein falsches Positiv handelt.

$("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
});
0
mahatrsna

EDIT: Meine frühere Antwort (basierend auf den Antworten in diesem Thread) war nicht der richtige Weg für mich. Ich wollte, dass ein Untermenü beim Eingeben oder Berühren mit der Maus erweitert und beim Verlassen der Maus oder einem anderen Berührungsklick ausgeblendet wird. Da Mausereignisse normalerweise nach Berührungsereignissen ausgelöst werden, war es etwas schwierig, Ereignis-Listener zu schreiben, die sowohl Touchscreen- als auch Mauseingaben gleichzeitig unterstützen.

jQuery-Plugin: Touch oder Maus

Am Ende habe ich ein jQuery-Plugin namens " Touch Or Mouse " (897 Byte) geschrieben, das erkennen kann, ob ein Ereignis von einem Touchscreen oder einer Maus aufgerufen wurde (ohne auf Touch-Unterstützung zu testen!). Dies ermöglicht die Unterstützung von Touchscreen und Maus zur gleichen Zeit und die vollständige Trennung ihrer Ereignisse.

Auf diese Weise kann das OP touchstart oder touchend zum schnellen Reagieren auf Berührungsklicks und click zum Aufrufen von Klicks only mit der Maus verwenden.

Demonstration

Zuerst muss man nämlich machen. Das Körperelement verfolgt Berührungsereignisse:

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

Mausereignisse werden standardmäßig an Elemente gebunden und durch Aufrufen von $body.touchOrMouse('get', e) können wir herausfinden, ob das Ereignis von einem Touchscreen oder einer Maus aufgerufen wurde.

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

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

Siehe das Plugin bei der Arbeit unter http://jsfiddle.net/lmeurs/uo4069nh .

Erläuterung

  1. Dieses Plugin muss aufgerufen werden, dh. das body-Element, um touchstart- und touchend -Ereignisse zu verfolgen, auf diese Weise muss das touchend -Ereignis nicht auf das Trigger-Element (dh einen Link oder eine Schaltfläche) ausgelöst werden. Zwischen diesen beiden Berührungsereignissen betrachtet dieses Plugin jedes Mausereignis als durch Berührung aufgerufen.
  2. Mausereignisse werden nur nach touchend ausgelöst. Wenn ein Mausereignis innerhalb von ghostEventDelay (Option, standardmäßig 1000 ms) nach touchend ausgelöst wird, berücksichtigt dieses Plug-in das Mausereignis durch Berührung aufgerufen werden.
  3. Wenn Sie mit einem Touchscreen auf ein Element klicken, erhält das Element den Status :active. Das Ereignis mouseleave wird erst ausgelöst, nachdem das Element diesen Zustand durch ie verloren hat. Klicken Sie auf ein anderes Element. Da dies Sekunden (oder Minuten!) Nach dem Auslösen des Ereignisses mouseenter sein kann, verfolgt dieses Plugin das letzte Ereignis mouseenter eines Elements: Wenn das letzte Ereignis mouseenter war Durch Berührung aufgerufen, wird das folgende mouseleave -Ereignis auch als durch Berührung aufgerufen betrachtet.
0
lmeurs

Hier ist eine einfache Möglichkeit, dies zu tun:

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

Grundsätzlich speichern Sie den ersten Ereignistyp, der für die Eigenschaft 'trigger' ausgelöst wird, im Datenobjekt von jQuery, das an das Stammdokument angehängt ist, und führen ihn nur aus, wenn der Ereignistyp dem Wert in 'trigger' entspricht. Auf Touch-Geräten wäre die Ereigniskette wahrscheinlich "Touchstart", gefolgt von "Klick". Der 'click'-Handler wird jedoch nicht ausgeführt, da "click" nicht dem in' trigger '("touchstart") gespeicherten ursprünglichen Ereignistyp entspricht.

Die Annahme, und ich glaube, es ist eine sichere, ist, dass Ihr Smartphone nicht spontan von einem Touch-Gerät zu einem Maus-Gerät wechselt oder dass das Tippen nie registriert wird, da der Ereignistyp "Auslöser" nur einmal pro gespeichert wird Page Load und "Click" würden niemals mit "Touchstart" übereinstimmen.

Hier ist ein Codepen, mit dem Sie herumspielen können (versuchen Sie, auf die Schaltfläche eines Touch-Geräts zu tippen - es sollte keine Klickverzögerung geben): http://codepen.io/thdoan/pen/xVVrOZ

Ich habe dies auch als einfaches jQuery-Plugin implementiert, das auch die Filterung von jQuery-Nachkommen durch Übergabe einer Auswahlzeichenfolge unterstützt:

// 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/

0
thdoan

finden Sie den Unterschied zwischen horizontaler und vertikaler Bildlaufbewegung des Dokuments. Berühren Sie den Startpunkt und den Endpunkt. Wenn einer von ihnen größer als 1 Pixel ist, wird er eher verschoben als angeklickt

var touchstartverscrollpos , touchstarthorscrollpos;


    $('body').on('touchstart','.thumbnail',function(e){

        touchstartverscrollpos = $(document).scrollTop();
        touchstarthorscrollpos = $(document).scrollLeft();


    });



    $('body').on('touchend','.thumbnail',function(e){


        var touchendverscrollpos = $(document).scrollTop();
        var touchendhorscrollpos = $(document).scrollLeft();

        var verdiff = touchendverscrollpos - touchstartverscrollpos;
        var hordiff = touchendhorscrollpos - touchstarthorscrollpos;


        if (Math.abs(verdiff) <1 && Math.abs(hordiff)<1){

// do you own function () here 



            e.stopImmediatePropagation();

            return false;
        }

    });
0
chings228

Dies wurde hier nicht erwähnt, aber Sie können diesen Link überprüfen: https://joshtronic.com/2015/04/19/handling-click-and-touch-events-on-the- Gleiches Element /

Um für die Nachwelt zu rekapitulieren, anstatt zu versuchen, beide Handler zuzuweisen und dann das Ergebnis zu sortieren, können Sie einfach prüfen, ob das Gerät ein Touchscreen ist oder nicht, und nur das relevante Ereignis zuweisen. Beobachten:

var clickEvent = (function() {
  if ('ontouchstart' in document.documentElement === true)
    return 'touchstart';
  else
    return 'click';
})();

// and assign thusly:

el.addEventListener( clickEvent, function( e ){ 
    // things and stuff
});

Ich verwende dies, um meine Ereignisse zu binden, damit ich auf Touchscreens testen kann, die sowohl touchstart als auch click Ereignisse verarbeiten, die zweimal ausgelöst würden, und auf meinem Entwicklungs-PC, der nur click

Ein Problem, das der Autor dieses Links erwähnt, sind Touchscreen-Laptops, die für beide Ereignisse ausgelegt sind:

Ich erfuhr von einem dritten Gerät, das ich nicht in Betracht zog, dem Touchscreen-Laptop. Es handelt sich um ein Hybridgerät, das sowohl Touch- als auch Click-Ereignisse unterstützt. Das Binden eines Ereignisses bedeutet, dass nur dieses Ereignis unterstützt wird. Bedeutet das, dass jemand mit einem Touchscreen und einer Maus explizit berühren muss, da dies das einzige Ereignis ist, das ich bearbeite?

Die Bindung von touchstart und click schien ideal für diese Hybridgeräte zu sein. Um zu verhindern, dass das Ereignis zweimal ausgelöst wird, habe ich den Rückruffunktionen e.stopPropagation() und e.preventDefault() hinzugefügt. e.stopPropagation() verhindert, dass Ereignisse zu ihren Eltern "sprudeln", verhindert aber auch, dass ein zweites Ereignis ausgelöst wird. Ich habe e.preventDefault() als "nur für den Fall" eingefügt, aber es scheint, als könnte es weggelassen werden.

0
Abraham Brookes

Ich arbeite auch an einer Android/iPad-Web-App, und es scheint, dass die Verwendung von "touchmove" zum "Verschieben von Komponenten" ausreicht (kein Touchstart erforderlich). Durch Deaktivieren von Touchstart können Sie .click () verwenden. von jQuery. Es funktioniert tatsächlich, weil es nicht durch Touchstart überlastet wird.

Schließlich können Sie binb .live ("touchstart", function (e) {e.stopPropagation ();}); Um das touchstart-Ereignis aufzufordern, sich nicht mehr zu verbreiten, klicken Sie im Wohnzimmer auf (), um ausgelöst zu werden.

Es hat bei mir funktioniert.

0
nembleton

Es gibt viele Dinge zu beachten, wenn Sie versuchen, dieses Problem zu lösen. Die meisten Lösungen brechen das Scrollen ab oder behandeln Ghost-Click-Ereignisse nicht richtig.

Eine vollständige Lösung finden Sie unter https://developers.google.com/mobile/articles/fast_buttons

NB: Sie können Ghost-Click-Ereignisse nicht auf Elementbasis verarbeiten. Ein verzögerter Klick wird nach Bildschirmposition ausgelöst. Wenn Ihre Berührungsereignisse die Seite auf irgendeine Weise ändern, wird das Klickereignis an die neue Version der Seite gesendet.

0
SystemParadox

Es kann effektiv sein, den Ereignissen 'touchstart mousedown' oder 'touchend mouseup', um unerwünschte Nebenwirkungen bei der Verwendung von click zu vermeiden.

0
Steven Lu

Warum nicht die jQuery-Ereignis-API verwenden?

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

Ich habe dieses einfache Ereignis mit Erfolg genutzt. Es ist sauber, Namespace-fähig und flexibel genug, um Verbesserungen vorzunehmen.

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

Wenn Sie jQuery verwenden, hat Folgendes für mich ganz gut funktioniert:

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

Andere Lösungen sind ebenfalls gut, aber ich habe mehrere Ereignisse in einer Schleife verknüpft und brauchte die Selbstaufruffunktion, um einen geeigneten Abschluss zu erstellen. Außerdem wollte ich die Bindung nicht deaktivieren, da ich wollte, dass sie beim nächsten Klick/Touchstart aufgerufen werden kann.

Könnte jemandem in einer ähnlichen Situation helfen!

0

Erkennen Sie für einfache Funktionen einfach die Berührung oder klicken Sie auf Ich verwende den folgenden Code:

var element = $("#element");

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

Dies gibt "touch" zurück, wenn das Touchstart-Ereignis definiert ist, und "no touch", wenn nicht. Wie ich bereits sagte, ist dies ein einfacher Ansatz für Click/Tap-Ereignisse.

0
Rodrigo

UDPATE:

Ich habe an einer Implementierung gearbeitet, um sowohl Click- als auch Touchend-Ereignisse für dieselbe Funktion zu verwenden, und die Funktion blockiert Ereignisse effektiv, wenn sich der Typ ändert. Mein Ziel war es, eine reaktionsschnellere Anwendungsoberfläche zu haben. Ich wollte die Zeit für den Start des Ereignisses auf die UI-Feedbackschleife reduzieren.

Damit diese Implementierung funktioniert, wird davon ausgegangen, dass Sie alle zugehörigen Ereignisse auf "Klicken" und "Berühren" hinzugefügt haben. Auf diese Weise wird verhindert, dass einem Element das Sprudeln von Ereignissen vorenthalten wird, wenn beide Ereignisse ausgeführt werden müssen, die jedoch unterschiedlichen Typs sind.

Hier ist eine einfache API-basierte Implementierung, die ich zu Demonstrationszwecken vereinfacht habe. Es wird gezeigt, wie die Funktionalität für ein Reduzierungselement verwendet wird.

var tv = {
    /**
     * @method eventValidator()
     * @desc responsible for validating event of the same type.
     * @param {Object} e - event object
     * @param {Object} element - element event cache
     * @param {Function} callback - callback to invoke for events of the same type Origin
     * @param {Object} [context] - context to pass to callback function
     * @param {Array} [args] - arguments array to pass in with context. Requires context to be passed
     * @return {Object} - new event cache
     */
    eventValidator: function(e, element, callback, context, args){
        if(element && element.type && element.type !== e.type){
            e.stopPropagation();
            e.preventDefault();
            return tv.createEventCacheObj({}, true);
        } else {
            element = tv.createEventCacheObj(e);
            (typeof context === "object" ? callback.apply(context, args) : callback());
            return element;
        }
    },

    /**
     * @method createEventCacheObj()
     * @param {Object} event - event object
     * @param {String} [event.type] - event type
     * @param {Number} [event.timeStamp] - time of event in MS since load
     * @param {Boolean} [reset=false] - flag to reset the object
     * @returns {{type: *, time: string}}
     */
    createEventCacheObj: function (event, reset){
        if(typeof reset !== 'boolean') reset = false;
        return {
            type: !reset ? event.type : null,
            time: !reset ? (event.timeStamp).toFixed(2): null
        };
    }
};

// Here is where the magic happens
var eventCache = [];
var pos = 0;

var $collapses = document.getElementsByClassName('tv-collapse__heading');
    Array.prototype.forEach.call($collapses, function(ele){
        ele.addEventListener('click', toggleCollapse);
        ele.addEventListener('touchend', toggleCollapse);

        // Cache mechanism
        ele.setAttribute('data-event-cache', String(pos++));
    });

/**
 * @func toggleCollapse()
 * @param {Object} e - event object
 * @desc responsible for toggling the state of a collapse element
 */
function toggleCollapse(e){
    eventCache[pos] = tv.eventValidator(e, eventCache[pos], function(){
       // Any event which isn't blocked will run the callback and its content
       // the context and arguments of the anonymous function match the event function context and arguments (assuming they are passed using the last two parameters of tv.eventValidator)

    }, this, arguments);
}

Ursprüngliche Antwort:

Hier ist eine Antwort, die eine Modifikation von Rafael Fragoso answer - pure JS ist.

(function(){
  
  button = document.getElementById('sayHi');
  
  button.addEventListener('touchstart', ohHai);
  button.addEventListener('click', ohHai);

  function ohHai(event){
    event.stopPropagation();
    event.preventDefault();
    console.log('ohHai is:', event.type);
  };
})();
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SO - Answer</title>
</head>
<body>
  <button id="sayHi">Anyone there?</button>
</body>
</html>

Führen Sie das Snippet auf den folgenden Seiten aus und achten Sie auf die Ausgabe:

  • Telefon
  • Tablette
  • Tablet (Desktop-Modus - falls zutreffend)
  • Desktop
  • Desktop (Touchscreen - falls zutreffend)

Der Schlüssel ist, dass wir verhindern, dass aufeinanderfolgende Ereignisse ausgelöst werden. Mobile Browser tun ihr Bestes, um einen Klick zu emulieren, wenn eine Berührung auftritt. Ich wünschte, ich könnte den Link zu einem Artikel vor einiger Zeit finden, der alle Ereignisse erklärt, die nach dem Berührungsstart durch Klicken auftreten. (Ich habe nach einer Verzögerung von 300 ms zwischen zweimaligem Antippen und Klicken gesucht).

Touch- und Mausgeräte

Ich habe einige Tests mit einem Surface Pro und einem Windows 10-Desktop mit Touchscreen durchgeführt. Was ich fand, war, dass beide Ereignisse auslösten, wie Sie vermuten, Touchstart für Berührungen und Klick für Trackpad, Maus und Stylist. Das Interessante war, dass ein Berührungsereignis in der Nähe, aber nicht auf der Schaltfläche, ein Klickereignis ohne Berührungsereignis auslösen würde. Es scheint, dass die integrierte Funktionalität in Windows 10 nach den nächsten Knoten in einem Umkreis sucht. Wenn ein Knoten gefunden wird, wird ein mausbasiertes Ereignis ausgelöst.

Mehrere Ereignisse desselben Typs

Wenn sich zwei Ereignisse desselben Typs auf einem Element befinden, kann das Auslösen eines der Ereignisse verhindert werden, wenn das Ereignis nicht mehr in die Luft sprudelt. Es gibt verschiedene Möglichkeiten, dies mit einem Cache zu erledigen. Meine anfänglichen Gedanken waren, das Ereignisobjekt zu modifizieren, aber wir erhalten eine Referenz, sodass ich denke, dass eine Cache-Lösung ausreichen muss.

0
curtismorte

Unter Ausnutzung der Tatsache, dass ein Klick immer einem Berührungsereignis folgt, habe ich Folgendes getan, um den "Geister-Klick" zu beseitigen, ohne Timeouts oder globale Flags verwenden zu müssen.

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

Grundsätzlich wird jedes Mal, wenn ein ontouchstart -Ereignis für das Element ausgelöst wird, ein Flag gesetzt und anschließend entfernt (und ignoriert), wenn der Klick erfolgt.

0
BigMacAttack