3分钟搞懂FastClick原理解析

垃圾回收官
• 阅读 8483

为什么要用FastClick

在移动端H5开发过程中,关于点触可能会遇到如下两个问题:

  • 手动点击与真正触发click事件会存在 300ms 的延迟
  • 点击穿透问题(点击行为会穿透元素触发非父子关系元素的事件)

延迟的存在时因为浏览器想知道你是否在进行双击操作;而点击穿透是因为 300ms 延迟触发时的副作用。

原理过程

  // 业务代码
  var $test = document.getElementById('test')
  $test.addEventListener('click', function () {
    console.log('1 click')
  })

  // FastClick简单实现
  var targetElement = null
  document.body.addEventListener('touchstart', function () {
    // 关键点1:记录点击的元素
    targetElement = event.target
  })
  document.body.addEventListener('touchend', function (event) {
    // 关键点2:阻止默认事件(屏蔽之后的click事件)
    event.preventDefault()
    var touch = event.changedTouches[0]
    // 关键点3:合成click事件,并添加可跟踪属性forwardedTouchEvent
    var clickEvent = document.createEvent('MouseEvents')
    clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null)
    clickEvent.forwardedTouchEvent = true // 自定义的
    targetElement.dispatchEvent(clickEvent)
  })

原理说明:
在document.body 上绑定 touchstart 和 touchend
其中,touchstart 用于记录当前点击元素的targetElement。
touchend 用于:

  • 组织默认事件(屏蔽之后的click事件)
  • 合成click事件,并添加可跟踪属性forwardedTouchEvent
  • 在 targetElement 上触发 click 事件
  • targetElement 上绑定的事件立即执行!done

点击穿透

比如,页面上有A和B元素,B在A上面。在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。
3分钟搞懂FastClick原理解析
移动端上面,事件执行顺序是:touchstart > touchend > click。而 click 事件有300ms延迟,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。

其他解决方案

禁用缩放
当HTML文档头部包含如下meta标签时:

<meta name="viewport" content="user-scalable=no">
<meta name="viewport" content="initial-scale=1,maximum-scale=1">

表明这个页面是不可缩放的,那双击缩放的功能就没有意义了,此时浏览器可以禁用默认的双击缩放行为并且去掉300ms的点击延迟。

这个方案有一个缺点,就是必须通过完全禁用缩放来达到去掉点击延迟的目的,然而完全禁用缩放并不是我们的初衷,我们只是想禁掉默认的双击缩放行为,这样就不用等待300ms来判断当前操作是否是双击。但是通常情况下,我们还是希望页面能通过双指缩放来进行缩放操作,比如放大一张图片,放大一段很小的文字。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Karen110 Karen110
3年前
盘点JavaScript focus/blur(聚焦)实际应用
大家好,我进阶学习者。一、前言当用户点击某个元素或使用键盘上的Tab键选中时,该元素将会获得聚焦(focus)。当网页加载时,HTML特性(attribute)。autofocus也可以让一个焦点落在元素上,不仅如此,还有其它途径可以获得焦点。二、focus/blur事件当元素聚焦时,会触发focus事件,当元素失去焦点时,会触发blur事件。
Stella981 Stella981
3年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Stella981 Stella981
3年前
Redis如何保证接口的幂等性?
在最近的一次业务升级中,遇到这样一个问题,我们设计了新的账户体系,需要在用户将应用升级之后将原来账户的数据手动的同步过来,就是需要用户自己去触发同步按钮进行同步,因为有些数据是用户存在自己本地的。那么在这个过程中就存在一个问题,要是因为网络的问题,用户重复点击了这个按钮怎么办?就算我们在客户端做了一些处理,在同步的过程中,不能再次点击,但是经过我最近
Stella981 Stella981
3年前
Noark入门之异步事件
引入异步事件主要是为了各模块的解耦,每当完成一个动作时,向系统发布一个事件,由关心的模块自己监听处理,可选择同步处理,异步处理,延迟处理。何时发布事件,当其他模块关心此动作时<br比如获得道具时,任务系统模块要判定完成进度,BI模块需要上报等等都可以监听此事件,已达模块解耦0x00事件源一个实现xyz.noark.core.event
Stella981 Stella981
3年前
JavaScript 基础(四)
HTMLDOMEvent(事件)HTML4.0的新特性之一是有能力使HTML事件触发浏览器中的动作(action),比如当用户点击某个HTML元素时启动一段JavaScript。下面是一个属性列表,这些属性可插入HTML标签来定义事件动作。onclick//当用户点
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
点击按住说话按钮事件有延迟
问题原因:该问题原因是由于系统的某些手势delaysTouchesBegan属性为YES,当按钮处在某些特定位置时触摸事件会先被这些系统的手势拦截,系统不响应才会继续分发,而按钮的UIControlEventTouchDown事件是需要立即响应的,所以会导
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(