Developing on the webkit for iPhone I encountered a curious delay ononClick events. It seems that the click is triggered with about 300 milliseconds delay. While this is unnoticeable on a standard web page, it can be annoying on a web application. Fortunately the click event can be overridden thus eliminating the delay.

I assume that 300ms is the time frame Apple guesses an user needed to perform gestures, but there are situations where this delay can be really annoying. Think to a calculator application with 300ms delay each time you press a button. Unacceptable.

The simplest solution is to use onTouchStart instead of onClick events. Something like <div ontouchstart="doSomething()"> is perfectly logical and overrides the onClick delay. But the action is triggered as soon as you touch the screen and may end up to undesirable results, so I tried to recreate the mouseDown/mouseUp events sequence with touchStart/touchMove/touchEnd.

Point your iPhone or simulator to my demo page. Clicking on the first button the standard click event is fired (with infamous 300ms delay), the second button instead overrides the onClick event and the action is actually cast on touchEnd with no delay.

The code I use is the following:

function NoClickDelay(el) {
this.element = el;
if( window.Touch ) this.element.addEventListener('touchstart', this, false);
} NoClickDelay.prototype = {
handleEvent: function(e) {
switch(e.type) {
case 'touchstart': this.onTouchStart(e); break;
case 'touchmove': this.onTouchMove(e); break;
case 'touchend': this.onTouchEnd(e); break;
}
}, onTouchStart: function(e) {
e.preventDefault();
this.moved = false; this.element.addEventListener('touchmove', this, false);
this.element.addEventListener('touchend', this, false);
}, onTouchMove: function(e) {
this.moved = true;
}, onTouchEnd: function(e) {
this.element.removeEventListener('touchmove', this, false);
this.element.removeEventListener('touchend', this, false); if( !this.moved ) {
// Place your code here or use the click simulation below
var theTarget = document.elementFromPoint(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
if(theTarget.nodeType == 3) theTarget = theTarget.parentNode; var theEvent = document.createEvent('MouseEvents');
theEvent.initEvent('click', true, true);
theTarget.dispatchEvent(theEvent);
}
}
};

The script creates a touchStart event and performs the click action on touchEnd which occurs 300ms before the standard click event. This is just an example to get you started, my function triggers the click event on touchEnd so you still need to add an onClick event (or an Anchor) somewhere if you want something to happen. You could better place directly your code on touchEnd but if you use my method your application will be compatible with both touch (the iphone) and non-touch enabled devices (the standard browser).

To activate the script all you need to do is: new NoClickDelay(document.getElementById('element'));. From now on all your clicks inside the element will be performed with no delay.

Note that you don’t need to apply the NoClickDelay() function to all the objects in the page, but just to a container. If for instance you have an unordered list, you don’t need to add the script to each <li>elements, but just to the <ul>. This has been done to reduce the number of event listeners so less resources are needed.

To closely mimic the standard UI you could add a hover class on touchStart to highlight the pressed object in someway and remove it on touchMove. (Apple places a gray rectangle over pressed elements).

Update 2009/02/27: By popular demand here follows the code that assigns the “pressed” CSS class to the clicked element.

function NoClickDelay(el) {
this.element = typeof el == 'object' ? el : document.getElementById(el);
if( window.Touch ) this.element.addEventListener('touchstart', this, false);
} NoClickDelay.prototype = {
handleEvent: function(e) {
switch(e.type) {
case 'touchstart': this.onTouchStart(e); break;
case 'touchmove': this.onTouchMove(e); break;
case 'touchend': this.onTouchEnd(e); break;
}
}, onTouchStart: function(e) {
e.preventDefault();
this.moved = false; this.theTarget = document.elementFromPoint(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
if(this.theTarget.nodeType == 3) this.theTarget = theTarget.parentNode;
this.theTarget.className+= ' pressed'; this.element.addEventListener('touchmove', this, false);
this.element.addEventListener('touchend', this, false);
}, onTouchMove: function(e) {
this.moved = true;
this.theTarget.className = this.theTarget.className.replace(/ ?pressed/gi, '');
}, onTouchEnd: function(e) {
this.element.removeEventListener('touchmove', this, false);
this.element.removeEventListener('touchend', this, false); if( !this.moved && this.theTarget ) {
this.theTarget.className = this.theTarget.className.replace(/ ?pressed/gi, '');
var theEvent = document.createEvent('MouseEvents');
theEvent.initEvent('click', true, true);
this.theTarget.dispatchEvent(theEvent);
} this.theTarget = undefined;
}
};

Are you aware of any simpler solution?

/SHARE THE JOY

/REACTIONS

      • AUTHOR: CONSTANTINE MUREEV
      • POSTED ON: 2012/08/20
      • AT: 08:25
      • AUTHOR: LUIS
      • POSTED ON: 2012/08/26
      • AT: 15:06

      hi,

      I’ve tried your code to improve the behavior of the JQM events and the delay which they have after clicking a button element, but I have a problem: after clicking the class ui-btn-active is still on the element and it isn’t removed. I’m not a totally newbie on JQ but, but I’m learning about the events handler, so, I’ll appreciate a little help.

      Thanks in advance!
      Luis Miguel.

      REPLY

    • Great bit of code.

      I modified it a bit to allow the finger to move up to 50 pixels before canceling the click.

      I am confused with .prototype. It seems that somehow handleEvent gets called. How?


      function NoClickDelay(el) {
      this.element = typeof el == 'object' ? el : document.getElementById(el);
      if( window.Touch ) this.element.addEventListener('touchstart', this, false);
      }

      NoClickDelay.prototype = {
      handleEvent: function(e) {
      switch(e.type) {
      case 'touchstart': this.onTouchStart(e); break;
      case 'touchmove': this.onTouchMove(e); break;
      case 'touchend': this.onTouchEnd(e); break;
      }
      },
      onTouchStart: function(e) {
      e.preventDefault();
      this.moved = false;
      this.x = e.targetTouches[0].clientX;
      this.y = e.targetTouches[0].clientY;
      this.theTarget = document.elementFromPoint(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
      if(this.theTarget.nodeType == 3) this.theTarget = theTarget.parentNode;
      this.theTarget.className+= ' pressed';
      this.element.addEventListener('touchmove', this, false);
      this.element.addEventListener('touchend', this, false);
      },
      onTouchMove: function(e) {
      var x = e.targetTouches[0].clientX;
      var y = e.targetTouches[0].clientY;
      if( Math.sqrt(Math.pow(x-this.x,2)+Math.pow(y-this.y,2))>50){
      this.moved = true;
      this.theTarget.className = this.theTarget.className.replace(/ ?pressed/gi, '');
      this.theTarget.className = this.theTarget.className.replace(/ ?active/gi, '');
      } else {
      if(this.moved==true){
      this.moved=false;
      this.theTarget.className+= ' pressed';
      }
      }
      },
      onTouchEnd: function(e) {
      this.element.removeEventListener('touchmove', this, false);
      this.element.removeEventListener('touchend', this, false);
      if( !this.moved && this.theTarget ) {
      this.theTarget.className = this.theTarget.className.replace(/ ?pressed/gi, '');
      this.theTarget.className+= ' active';
      var theEvent = document.createEvent('MouseEvents');
      theEvent.initEvent('click', true, true);
      this.theTarget.dispatchEvent(theEvent);
      }
      this.theTarget = undefined;
      }
      };

      REPLY

    • Nice script! I made it into a jQuery plug-in and added a function to make the script set focus on the element selected after firing the click event. I found this fixes an issue where input fields are not properly selected on an iPhone if they are within the NoClickDelay element.

      Code:

      (function( $ ) {
      $.fn.noClickDelay = function() {

      var $wrapper = this;
      var $target = this;
      var moved = false;

      $wrapper.bind('touchstart mousedown',function(e) {
      e.preventDefault();
      moved = false;
      $target = $(e.target);
      if($target.nodeType == 3) {
      $target = $($target.parent());
      }
      $target.addClass('pressed');

      $wrapper.bind('touchmove mousemove',function(e) {
      moved = true;
      $target.removeClass('pressed');
      });

      $wrapper.bind('touchend mouseup',function(e) {
      $wrapper.unbind('mousemove touchmove');
      $wrapper.unbind('mouseup touchend');
      if(!moved && $target.length) {
      $target.removeClass('pressed');
      $target.trigger('click');
      $target.focus();
      }
      });
      });

      };
      })( jQuery );

      Use as such:
      $('#wrapperElement').noClickDelay();

      REPLY

      • AUTHOR: MUNDI
      • POSTED ON: 2013/01/26
      • AT: 22:55

      I my iphone4 I get a click delay of 560ms!!

      What are apple smoking?

      Even with your improvement its still 153ms seconds… better than 560.

      REPLY

       
       
      http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone

REMOVE ONCLICK DELAY ON WEBKIT FOR IPHONE的更多相关文章

  1. iOS混合应用开发入门

    原文出处: cocoacontrols   译文出处:魏志峰(@JeremyWei)   欢迎分享原创到伯乐头条 http://blog.jobbole.com/46554/ 介绍 上周(译者:原文成 ...

  2. 10、QT分析之WebKit

    该文章整理自 网易博客 http://blog.163.com/net_worm/blog/static/12770241920101831312381/ 转载请注明出处 WebKit是QT4新整合的 ...

  3. QT分析之WebKit

    该文章整理自 网易博客 http://blog.163.com/net_worm/blog/static/12770241920101831312381/ 转载请注明出处 WebKit是QT4新整合的 ...

  4. webkit事件处理

    1,概述 原文地址:http://blog.csdn.net/awebkit/article/details/8493716 浏览器处理事件一般有两个过程,捕获过程和冒泡过程,这是由addEventL ...

  5. 八大Webkit内核浏览器

    列举出时下最流行的Webkit内核浏览器,所以我们并不会做出评测和对比.PS:本文列举的浏览器有一部分为IE+Webkit双核浏览器,如果您对其他IE内核浏览器很感兴趣<抛弃数据!用体验和感觉告 ...

  6. 浅谈关于QT中Webkit内核浏览器

    关于QT中Webkit内核浏览器是本文要介绍的内容,主要是来学习QT中webkit中浏览器的使用.提起WebKit,大家自然而然地想到浏览器.作为浏览器内部的主要构件,WebKit的主要工作是渲染.给 ...

  7. [backbone] Getting Started with Backbone.js

    一.简介 Backbone 是一个 JavaScript MVC 框架,它属于轻量级框架,且易于学习掌握.模型.视图.集合和路由器从不同的层面划分了应用程序,并负责处理几种特定事件.处理 Ajax 应 ...

  8. SqLiter

    1.去重 select *  from daydata where wtid||rectime in (select wtid||rectime from daydata group by wtid| ...

  9. 一些特殊css

    属性 描述            outline  (轮廓)是绘制于元素周围的一条线,位于边框边缘的外围,可起到突出元素的作用. outline:#00FF00 dotted thick; 可以按顺序 ...

随机推荐

  1. Unity3D 事件

    unity3d事件函数整理,事件,回调函数,消息处理 Unity3D中所有控制脚本的基类MonoBehaviour有一些虚函数用于绘制中事件的回调,也可以直接理解为事件函数,例如大家都很清楚的Star ...

  2. IOS 设置导航栏全局样式

    // 1.设置导航栏背景 UINavigationBar *bar = [UINavigationBar appearance]; [bar setBackgroundImage:[UIImage r ...

  3. .net操作数据库,史上最牛逼的方法,你见过这种方法吗

    免费分享给大家.下载地址在最下面. C# code   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ...

  4. Python-8 元组tuple

    #1 特殊的列表:元组 元组中的元素不可改变 #2 创建.访问 >>> tuple1=(1,2,3) >>> tuple1=1,2,3 >>> t ...

  5. (转)关于URLDownloadToFile下载文件

    转自:http://zhouhaijiang3.blog.163.com/blog/static/43477220200931981322497/ 在下载文件时,下载文件的目录大小写要注意和虚拟目录的 ...

  6. 关于Windows 7启动后网络一直转的问题的一个解决方法

    前两天给自己的X220i换了个SSD,装了个windows 7 的系统,然后自然搞了一些杀毒软件之类的,为了开发需要还装了Oracle数据库,可是,不知道从什么时候开始,系统启动后右下角那个网络图标显 ...

  7. jquery 农历日历 可自适应

    在网上找了许多大牛做的农历日历,但是不是不符合项目中的要求,就是本身就有问题有Bug ,把大牛门的做日历看了n遍 自己又改造了一遍得到了这个:随后日历又要做个自适应的长宽,又在js中改造代码..... ...

  8. MyBatis的应用

    a)首先先导入固定的jar包 b)添加mybatis配置文件mybatis-config.xml 1.添加mybatis配置文件mybatis-config.xml <?xml version= ...

  9. jquery easyui校验select下拉列表值是否为空的问题

    属性名 类型 描述 默认值 required 布尔 定义文本域是否为必填项 false validType 字符串 定义字段的验证类型,比如email, url, etc. null missingM ...

  10. centos 6.5 x64创建并挂载使用iscsi共享磁盘

    前景摘要:NFS或iSCSI,哪个更好?文件 vs 块NFS使用文件级别的实施,服务器或存储阵列托管整个文件系统,客户到文件系统上读写文件,可以在阵列端对主存储数据进行重复数据删除.iSCSI和FC则 ...