9

When viewing a web site on a mobile device (iPad, Galaxy Tab) there's always a lag when I click an element (regular link or anything else that is made clickable using javascript/jquery).

While reading online, I found out that the browser is using touchstart followed by touchend events, and afterwards it triggers the regular click event. Is there a way to have a more responsive tap and remove the click event that is delayed? Maybe by using javascript, or something else?

Gabriel
  • 2,720
  • 4
  • 28
  • 36

3 Answers3

5

Adapted from Matt's library (https://stackoverflow.com/a/9370637/1491212) itself adapted from google code, I wrote a jQuery plugin.

Use like this : $('mySelector').fastClick(handler);

(function($){
    var clickbuster = {
        preventGhostClick: function(x, y) {
          clickbuster.coordinates.push(x, y);
          window.setTimeout(clickbuster.pop, 2500);
        },
        pop: function() {
          clickbuster.coordinates.splice(0, 2);
        },
        onClick: function(event) {
          for (var i = 0; i < clickbuster.coordinates.length; i += 2) {
            var x = clickbuster.coordinates[i];
            var y = clickbuster.coordinates[i + 1];
            if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
              event.stopPropagation();
              event.preventDefault();
            }
          }
        }
    };


    var methods = {
        init: function(handler){
            return this.each(function() {
                var $this = $(this),
                    data = $this.data('fastClick');
                if(!data){
                    this.addEventListener('touchstart', methods.handleEvent, false);
                    this.addEventListener('click', methods.handleEvent, false);
                    $this.data('fastClick', {
                        target: $this,
                        handler: handler
                    });
                }
            });
        },
        handleEvent:function(event) {
          switch (event.type) {
            case 'touchstart': $(this).fastClick('onTouchStart',event); break;
            case 'touchmove': $(this).fastClick('onTouchMove',event); break;
            case 'touchend': $(this).fastClick('onClick',event); break;
            case 'click': $(this).fastClick('onClick',event); break;
          }
        },
        onTouchStart: function(event) {
          event.stopPropagation();
          this[0].addEventListener('touchend', methods.handleEvent, false);
          var _this = this;
          document.body.addEventListener('touchmove', function(event){
            methods.handleEvent.apply(_this,[event]);
          }, false);

          $(this).data('fastClick').startX = event.touches[0].clientX;
          $(this).data('fastClick').startY = event.touches[0].clientY;
        },
        onTouchMove: function(event) {
          if (Math.abs(event.touches[0].clientX - this.data('fastClick').startX) > 10 ||
              Math.abs(event.touches[0].clientY - this.data('fastClick').startY) > 10) {
              this.fastClick('reset');
          }
        },
        onClick: function(event) {
          event.stopPropagation();
          $(this).fastClick('reset');
          $(this).data('fastClick').handler.call(this,event);

          if (event.type == 'touchend') {
            clickbuster.preventGhostClick($(this).data('fastClick').startX, $(this).data('fastClick').startY);
          }
        },
        reset: function() {
          this[0].removeEventListener('touchend', methods.handleEvent, false);
          document.body.removeEventListener('touchmove', methods.handleEvent, false);
        }
    }
    $.fn.fastClick = function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if ( typeof method === 'object' || typeof method === 'function' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.hScroll');
        }

    }

    clickbuster.coordinates = [];
    document.addEventListener('click', clickbuster.onClick, true);

})(jQuery);
Community
  • 1
  • 1
Armel Larcier
  • 15,747
  • 7
  • 68
  • 89
  • This seems to be cool, but I can't work out how to know what I clicked on – $(this) doesn't return the thing you click, unlike binding an event normally. `$('a').fastClick(function(){alert($(this).attr('href'))});` For instance – how might I get that to work? – Rich Bradshaw Nov 02 '12 at 16:08
  • I ran into this problem also. Just edited my answer. The onClick handler must be called in the right scope: handler.call(this,event); See edit – Armel Larcier Nov 03 '12 at 12:37
  • 1
    Also, is this required anymore? I saw a few articles suggesting that from Gingerbread onwards that if you disabled zooming on the viewport meta tag, touches didn't have the lag... Know anything about that? – Rich Bradshaw Nov 03 '12 at 13:22
  • Don't know about android but it is still needed for iOS – Armel Larcier Nov 03 '12 at 14:58
  • On iOS can't you just bind to `touch` instead of `click` - pretty sure that `touch` doesn't have the lag. – Rich Bradshaw Nov 03 '12 at 15:12
  • there is no native 'touch' event on iOS. The FastClick library (http://labs.ft.com/2011/08/fastclick-native-like-tapping-for-touch-apps/) takes advantage of 'touchstart' and 'touchend' events to dispatch the click event in the appropriate context and without delay. – Armel Larcier Nov 04 '12 at 10:42
  • Wow – looks like I was totally wrong about that! Where did I get the idea there was a 'touch' event? Slightly concerned about code I've written now… – Rich Bradshaw Nov 04 '12 at 11:01
2

if you are writing a web page your self you can register a listener for touchstart and touchend and trigger the onclick code directly from on touch end without any delay.

If you don`t handle the event in touch move the browser will dispatch (with some lag) a click event to the element

Take a look at this description from google to create "fast buttons": http://code.google.com/intl/de-DE/mobile/articles/fast_buttons.html

Daniel Kurka
  • 7,973
  • 2
  • 24
  • 43
  • It would be appreciated if you could post some code on how you would do that. I usually use jQuery when building websites and I know I can use .trigger() to trigger the click event. But I have no idea how I can automatically call "click" on each "tap" without adding it manually everytime I need it... – Gabriel Jan 30 '12 at 22:10
0

I use detection if the device support touch like modernizer. i fill a var called touchClick with the options 'click' or 'touchend' bases on the outcome if it is a touch device or not. In jquery I simply call:

 $('element').on(touchClick, function(e){ //do something });

It has a very small footprint.

Aurelio De Rosa
  • 21,856
  • 8
  • 48
  • 71
Plippie
  • 2,836
  • 33
  • 28
  • This approach is dangerous if the browser is used from a touchscreen enabled laptop. The mouse clicks would not work... – franzlorenzon Oct 08 '13 at 11:03