下面是一段非常简单的JavaScript代码

dianji

setTimeout(function () {

alert('timer handler')

}, 2000)

function test () {

document.addEventListener('click', function (e) {

alert('click handler')

}, false)

var startTime = new Date()

while ((new Date()).getTime() - startTime < 5000){}

}

但是当你点击这个按钮时,产生的效果可能会让你有些困惑。下面我们来看下:

页面打开2s内点击一次按钮

这段JavaScript代码当你在页面打开2s时间内点击一次按钮,效果是这样的:

页面卡住大约5s钟

大约5s后弹出 click handler

点击弹窗确认后,弹出 timer handler

当你继续再次点击页面中的按钮,此时依次发生:

页面卡住大约5s钟

大约5s后弹出 click handler 一次!

点击确认后又弹出 click handler 一次。

如果继续点击button按钮,会出现同样的效果,且 click handler 弹出的次数会依次增加

分析

分析这段代码来看,点击按钮后,代码执行会进入 test 函数, test函数中首先对 document 对象绑定上了一个 click 事件。然后执行了一个5s的死循环。

此时页面卡住就是因为这个死循环

ar startTime = new Date()

while ((new Date()).getTime() - startTime < 5000){}

这个死循环会导致js阻塞在这里. 在这5s时间内,2s的定时器其实在第2秒的时候已经定时完成,并把这个完成的事件放入到了任务队列中;而你在2秒之前点击的按钮这个click事件也被浏览器放入一个dom事件队列等待执行。

当5s死循环的时间过去,js引擎开始变成空闲,此时点击按钮触发的这个test处理器执行完毕,js引擎便从事件队列中取出 click 事件进行执行,当前元素没有订阅click那么就冒泡到订阅了该事件的document进行执行。(会继续冒泡到document。(本质上冒泡其实是: 浏览器取出dom事件中的click事件,然后从target元素开始往上找 看下是否整个网页中还有元素订阅了这个click事件)

由于在刚刚test函数执行期间,document对象上绑定上了 click 的监听,所以此时冒泡上来的 click 会触发document对象上的 click事件处理器, 因此弹出了 click handler.

当这个 click 冒泡完毕,所有的订阅者订阅的处理器都被完全处理完,js线程再次空闲,此时去查看任务队列中的任务,发现有个2s定时器的任务已经执行完毕,js开始执行定时器的回调函数,所以弹出了 time handler

当你第二次点击按钮,再次触发了 test 函数。 此时test函数内还是做了同样的事情,但是之前document上已经绑定了一个click的handler函数,所以第二次执行 test函数,会让 document对象的click处理器变成2个。 因此第二次点击按钮 click handler 会弹出2次

页面打开2s内点击一次按钮,然后第3s时点击页面空白处2次

这样操作的效果是这样的:

页面卡住大约5s钟

大约5s后弹出 'click handler'

弹出 'click handler' 第二次

弹出 'click handler' 第三次

弹出 'time handler'

分析

第2点之所以出现在第5点之前,在上文我们已经讲过原因了---总之,基本上是因为click触发的时刻确实就比timer触发的早,肯定要等click的handler都处理完再执行timer处理器。

但至于第3、4点为什么出现在5之前呢?这个跟2出现在5之前的原因就不一样了,因为用户在页面上的第二次和第三次点击是在2s钟之后了,此时timer定时器肯定已经完成了,但是触发 click handler 依然在 timer handler 之前。 这是为什么呢?

这主要是因为js获取任务来执行时, 点击事件的任务队列 要优先于 timer事件的任务队列。 具体可参考我的另外一篇文章 浏览器的单线程机制和事件循环

在页面卡住的5s时间内,用户在页面上点击的2次事件会放入比timer更优先的一个macroTask任务队列。由于js空闲时优先要把click事件这种更优先的macroTask任务执行完,直到任务队列为空。所以就出现了上面 click handler 要比 timer handler 更早弹出的效果。

心得

js中事件可以注册多个handler形成handlers. handlers类似于一个处理器的数组。事件触发后,该事件的handler处理器会被依次执行.

这里举个跟上面有点区别的例子:假如在某个handler执行的过程中,又给该事件增加了新的handler,那么新增的这个handler不能立即执行。

demo测试代码:

dianji

function test () {

alert('click handler 1');

/* test函数触发的过程中,又给按钮绑定了新的handler; 但本次handlers遍历执行的过程中,不会执行新加入的这个handler */

/* 因此,首次点击按钮, click handler2 不会弹出 */

document.querySelector('#test').addEventListener('click', function (e) {

alert('click handler 2')

}, false);

}

其实这里原理很简单:因为test元素对象上的事件handlers被触发执行的时候,类似于把数组拿出来遍历。你不可能把遍历数组和修改数组的逻辑同时运行。如:

let a = [1,2,3]

let count = 'x'

a.forEach((item, index) => {

console.log(item)

a.push(count + index)

})

console.log(a)

// 输出

// 1

// 2

// 3

// [ 1, 2, 3, 'x0', 'x1', 'x2' ]

除非你addEventListener的时候,添加到冒泡的上层元素上。即下面讲的第三点。

addEventListener 会给事件不断增加新的处理器handler

事件处理器handler在执行期间,事件还没有冒泡。此时还有机会给上层元素绑定事件处理器。

一个事件在冒泡过程中,要等所有订阅该事件的处理器都处理完毕,js才会去选择新的任务队列中的任务来执行。在事件触发后以及事件的冒泡过程中,会优先执行订阅了该冒泡事件的处理器,而不会去理会任务队列。

这一条原理很简单,只需知道:js在执行同一个dom事件的所有回调处理器的过程是同步的,占用js线程执行的即可。

在事件循环中 microTask 优先于 marcroTask执行,且macroTask中也有不同优先级的队列,例如dom事件便高于timer。

click事件在什么时候出发_剖析setTimeout和click点击事件的触发顺序相关推荐

  1. click事件在什么时候出发_超全的js事件机制amp;事件委托

    超全的js事件机制&事件委托,想要理解js事件只需认真看完此篇即可~ 目录结构: 什么是事件机制 事件冒泡事件捕获 DOM事件流事件委托 误区 在同一个对象上注册事件,并不一定按照注册顺序执行 ...

  2. click事件在什么时候出发_关于JS 事件冒泡和onclick,click,on()事件触发顺序

    今天在给JQgrid中的标签添加click事件的时候,发现一个问题. JQgrid的table中,点击任何位置,都会勾选点击行的checkbox,而我希望在点击我的标签的时候,不要勾选checkbox ...

  3. java的按钮点击事件_[转载]java处理按钮点击事件

    不同的事件源可以产生不同类别的事件.例如,按钮可以发送一个ActionEvent对象,而窗口可以发送WindowEvent对象. AWT时间处理机制的概要: 1.监听器对象是一个实现了特定监听器接口( ...

  4. jquery 监听td点击事件_安卓开发监听点击事件的一种方法

    本人是菜鸟一只,学习安卓纯属兴趣.没有真正上过编程课程,所有知识都是在网上获取的.今天分享的是监听点击事件的一个方法,这个方法的好处是代码较简洁. 如图,点击保存时,把上面的数据入库. 实现如下: 在 ...

  5. css隐藏元素 触发点击事件,css隐藏元素的几种方法中可以触发点击事件的是?...

    css隐藏元素的几种方法中可以触发点击事件的是 是opacity设置为0的方法. css隐藏元素的方式: 1.display:none;.box{ display: none; } 最简单也最粗暴的方 ...

  6. html 隐藏元素点击事件,css隐藏元素的几种方法中可以触发点击事件的是?

    css隐藏元素的几种方法中可以触发点击事件的是 是opacity设置为0的方法. css隐藏元素的方式: 1.display:none;.box{ display: none; } 最简单也最粗暴的方 ...

  7. android列表集合点击事件,给ListeView列表中的每一个Item添加点击事件

    首先声明本文主要是在ArrayAdapter.SimpleAdapter中对ListView的每一项进行点击事件! 先看下运行结果 第一步:在xml布局中写一个ListView android:lay ...

  8. html表格数据点击事件,如何在iview的table单元格里实现点击事件?

    需求是这样的,我需要做一个表格,表头和内容全都是动态的. 从服务器端获取到数据后,最后拼装成一个二维数组slotTableData,里面的内容是自 iview 2.3 支持的 html.这样就可以在界 ...

  9. iostext添加点击事件_iOS开发小技巧 - label中的文字添加点击事件

    Label中的文字添加点击事件 以前老师讲过类似的功能,自己懒得回头看了,找了很多第三方的,感觉这个小巧便利,作者只是扩展了分类,实现起来代码也少.先来个效果图 自己的项目,直接上代码 - (void ...

最新文章

  1. 087_改变html
  2. vc2019切分位图图像带动态创建工具条
  3. WinCE开发初探——开发环境
  4. MNIST 手写数字识别,我是如何做到886个可训练参数,识别率达到98.2%? (参数、模型压缩), Keras实现,模型优化
  5. 初识ABP vNext(8):ABP特征管理
  6. 在Java中模拟求和类型的巧妙解决方法
  7. ASP.NET小技巧——回传后保持页面的滚动位置
  8. 手机配置网络代理服务器_两张图简说代理服务器和反向代理服务器
  9. 无人驾驶的规划与控制(二)——行为决策规划
  10. Git-github 的基本应用
  11. Auto.js抓取移动端数据实战
  12. 构建区块链量化交易系统(一)
  13. [导入]MPQ 文件系统完成
  14. word打开html显示空白,电脑打开Word文档内容显示不全或显示空白怎么解决
  15. 中国二代居民身份证校验_编码正确性校验 java实现
  16. 声音大小与振幅的关系_物理知识问答-响度与振幅的关系是什么?
  17. 计算机中班音乐,幼儿园中班音乐活动课件大全【三篇】
  18. 【Hyperledger Fabric】学习笔记1—— 区块链介绍
  19. python csv 大文件_python 快速把超大txt文件转存为csv的实例
  20. c语言马踏棋盘编程分析,C语言马踏棋盘实现

热门文章

  1. 递归判断一个数是否递增
  2. 跟我学Telerik公司的RadControls控件(四)
  3. 牛客16732 序列(排列组合)
  4. 【剑指offer】面试题22:链表中倒数第 K 个节点
  5. python3.7和2.7_为两个python版本(2.7和3.7)Windows 10安装pip
  6. 小程序 pagescrollto_微信小程序学习笔记(三)-- 首页及详情页开发
  7. php登录个性验证码,PHP七种不同的个性创意验证码例子
  8. python十个运维实战项目_干货 | 这4个Python实战项目,让你瞬间读懂Python!
  9. Python实现一个数组除以一个数
  10. 工业机器人的控制方式