Is there a way to remove the clicking lag on mobile touch devices?
Asked Answered
G

3

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?

Grapheme answered 30/1, 2012 at 21:53 Comment(0)
M
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

Mediocrity answered 30/1, 2012 at 22:0 Comment(1)
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...Grapheme
S
5

Adapted from Matt's library (https://mcmap.net/q/219784/-click-delay-on-iphone-and-suppressing-input-focus) 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);
Stadiometer answered 19/10, 2012 at 7:51 Comment(7)
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?Dyslalia
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 editStadiometer
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?Dyslalia
Don't know about android but it is still needed for iOSStadiometer
On iOS can't you just bind to touch instead of click - pretty sure that touch doesn't have the lag.Dyslalia
there is no native 'touch' event on iOS. The FastClick library (labs.ft.com/2011/08/…) takes advantage of 'touchstart' and 'touchend' events to dispatch the click event in the appropriate context and without delay.Stadiometer
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…Dyslalia
M
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

Mediocrity answered 30/1, 2012 at 22:0 Comment(1)
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...Grapheme
H
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.

Hyrcania answered 12/11, 2012 at 9:3 Comment(1)
This approach is dangerous if the browser is used from a touchscreen enabled laptop. The mouse clicks would not work...Rojas

© 2022 - 2024 — McMap. All rights reserved.