超全的js事件机制&事件委托,想要理解js事件只需认真看完此篇即可~

目录结构:

什么是事件机制

事件冒泡事件捕获

DOM事件流事件委托

误区

在同一个对象上注册事件,并不一定按照注册顺序执行

event.stopPropagation();就是阻止事件的冒泡return false;阻止默认行为

拓展

stopImmediatePropagation 的使用setCapture 和 releaseCapture

参考文章

什么是事件机制

JavaScript 事件机制描述的是事件在 DOM 里面的传递顺序,以及我们可以对这些事件做出如何的响应。

DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。

事件捕获(event capturing): 通俗的理解就是,当鼠标点击或者触发dom事件时,浏览器会从根节点开始由外到内进行事件传播,即点击了子元素,如果父元素通过事件捕获方式注册了对应的事件的话,会先触发父元素绑定的事件。

事件冒泡(dubbed bubbling): 与事件捕获恰恰相反,事件冒泡顺序是由内到外进行事件传播,直到根节点。无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播,它就像一跟引线,只有通过引线才能将绑在引线上的鞭炮(事件监听器)引爆, 试想一下,如果引线不导火了,那鞭炮就只有一响了!!!

dom标准事件流的触发的先后顺序为 :先捕获再冒泡,即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡

不同的浏览器对此有着不同的实现,IE10及以下不支持捕获型事件,所以就少了一个事件捕获阶段,IE11、Chrome 、Firefox、Safari等浏览器则同时存在。

说到事件冒泡与捕获就不得不提一下两个用于事件绑定的方法addEventListener 、

attachEvent 。当然还有其它的事件绑定的方式这里不做介绍。

addEventListener(event, listener, useCapture)

·参数定义:event---(事件名称,如click,不带on), listener---事件监听函数,

useCapture---是否采用事件捕获进行事件捕捉, 默认为false,即采用事件冒泡方式

addEventListener在 IE11、Chrome 、Firefox、Safari等浏览器都得到支持。

attachEvent(event,listener)

·参数定义:event---(事件名称,如onclick,带on), listener---事件监听函数。

attachEvent主要用于IE浏览器,并且仅在IE10及以下才支持,IE11已经废了这个方法了

(微软还是挺识趣的,慢慢向标准靠拢)。

事件冒泡

事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的元素

<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>js事件机制</title>
<style>
#parent{width: 200px; height:200px;
text-align: center; line-height: 3;background: green;
}
#child{width: 100px; height: 100px; margin: 0 auto; background: orange;
}
</style>
</head>
<body>
<div id="parent"> 父元素
<div id="child"> 子元素
</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("parent"); var child = document.getElementById("child");document.body.addEventListener("click",function(e){ console.log("click-body");
},false);parent.addEventListener("click",function(e){ console.log("click-parent");
},false);child.addEventListener("click",function(e){ console.log("click-child");
},false);
</script>
</body>
</html>

通过"addEventListener"方法,采用事件冒泡方式给dom元素注册click事件,点击子元素会发生什么呢?如果你对事件冒泡有一定了解的话那你肯定知道上面的代码会输出的顺

序,没错,如下图所示:

事件触发顺序是由内到外的,这就是事件冒泡,虽然只点击子元素,但是它的父元素也会触发相应的事件,其实这是合理的,因为子元素在父元素里面,点击子元素也就相当于变相的点击了父元素,这样理解对吧?这里有同学可能要问了,如果点击子元素不想触发父元素的事件怎么办?肯定可以的,那就是停止事件传播---event.stopPropagation();

事件捕获

不太具体的节点更早接受事件,而最具体的元素最后接受事件,和事件冒泡相反修改上面栗子中的代码,给parent元素注册一个捕获事件,如下

var parent = document.getElementById("parent");
var child = document.getElementById("child");document.body.addEventListener("click",function(e){ console.log("click-body");
},false);parent.addEventListener("click",function(e){ console.log("click-parent---事件传播");
},false);
//新增事件捕获事件代码
parent.addEventListener("click",function(e){ console.log("click-parent--事件捕获");
},true);
child.addEventListener("click",function(e){ console.log("click-child");
},false);

如果你看明白了我前面说的那些,你就知道这个栗子的输出顺序了。

父元素通过事件捕获的方式注册了click事件,所以在事件捕获阶段就会触发,然后到了目标阶段,即事件源,之后进行事件传播,parent同时也用冒泡方式注册了click事件,所以这里会触发冒泡事件,最后到根节点。这就是整个事件流程。

DOM事件流

DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,时间冒泡阶段, 首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接受事件,最后是冒泡阶段

注:Opera、Firefox、Sarfari都支持DOM事件流,IE不支持事件流,只支持时间冒泡

当一个事件发生以后,它会在不同的DOM节点之间传播(propagation)。这种传播分为三个阶段:

第一阶段:从window对象传导到目标节点,称为“捕获阶段”(capture phase)。第二阶段:在目标节点上触发,称为“目标阶段”(target phase)。

第三阶段:从目标节点传导回window对象,称为“冒泡阶段”(bubbling phase)。

事件委托

事件委托其实也叫事件代理。

定义:事件代理就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。(delegation)。

var ul = document.querySelector('ul'); ul.addEventListener('click', function(event){if(event.target.tagName.toLowerCase() === 'li'){//...
}})

上面代码的click 事件的监听函数定义在<ul> 节点,但是实际上,它处理额是子节点<li> 的click事件。这样的好处是,只要定义一个监听函数,就能处理多个子节点的事件,且以后再添加子节点,监听函数依然有效。

那什么样的事件可以用事件委托,什么样的事件不可以用呢?

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress 。值得注意的是,mouseover 和mouseout 虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

更加详细内容可查看:Js 中事件绑定、事件代理和事件委托

误区

在同一个对象上注册事件,并不一定按照注册顺序执行

之所以如此是因为事件目的地节点既绑定了冒泡事件也绑定了捕获事件,此时的执行顺序按照绑定的先后顺序执行(情况比较少见)。

举例

<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>js事件机制</title>
<style>
#parent{width: 200px; height:200px;
text-align: center; line-height: 3; background: green;
}#child{width: 100px; height: 100px; margin: 0 auto; background: orange;
}
</style>
</head>
<body>
<div id="parent"> 父元素
<div id="child"> 子元素
</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("parent"); var child = document.getElementById("child");// document.body.addEventListener("click",function(e){
//  console.log("click-body");
// },false); child.addEventListener("click",function(e){ console.log("click-child");
},false);child.addEventListener("click",function(e){ console.log("click-child-捕获");
},true); parent.addEventListener("click",function(e){console.log("click-parent");
},false);parent.addEventListener("click",function(e){ console.log("click-parent-捕获");
},true);</script>
</body>
</html>

点击子DIV执行结果:

交换子div事件,如下:

<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>js事件机制</title>
<style>
#parent{width: 200px; height:200px;
text-align: center; line-height: 3; background: green;
}
#child{width: 100px; height: 100px; margin: 0 auto; background: orange;
}
</style>
</head>
<body>
<div id="parent"> 父元素
<div id="child"> 子元素
</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("parent"); var child = document.getElementById("child");// document.body.addEventListener("click",function(e){
//  console.log("click-body");
// },false);child.addEventListener("click",function(e){ console.log("click-child-捕获");},true);
child.addEventListener("click",function(e){ console.log("click-child");
},false);parent.addEventListener("click",function(e){ console.log("click-parent");
},false);parent.addEventListener("click",function(e){ console.log("click-parent-捕获");
},true);</script>
</body>
</html>

点击子DIV执行结果:

由于子DIV上绑定了捕获和冒泡事件,所以此时的执行顺序是按照绑定的执行顺序。实际这种情况很少。

event.stopPropagation();就是阻止事件的冒泡

这个表述不能说他错误,但是是不完整的,他除了阻止事件的冒泡,还阻止事件的继续捕获,简而言之就是阻止事件的进一步传播 。下面的例子可以看到:

<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>js事件机制</title>
<style>#parent{width: 200px; height:200px;
text-align: center; line-height: 3; background: green;
}
#child{width: 100px; height: 100px; margin: 0 auto; background: orange;
}
</style>
</head>
<body>
<div id="parent"> 父元素
<div id="child"> 子元素
</div>
</div>
<script type="text/javascript">
var parent = document.getElementById("parent"); var child = document.getElementById("child");child.addEventListener("click",function(e){ console.log("click-child-捕获");
},true);
//  child.addEventListener("click",function(e){
//  console.log("click-child");
// },false);// parent.addEventListener("click",function(e){
//  console.log("click-parent");
// },false);parent.addEventListener("click",function(e){ event.stopPropagation();
console.log("click-parent-捕获");
},true);</script>
</body>
</html>

执行结果:

return false;阻止默认行为

return false;事件处理过程中,阻止了事件冒泡,也阻止了默认行为(比如刚才它就没有执行超链接的跳转)

return false 不仅阻止了事件往上冒泡,而且阻止了事件本身。event.stopPropagation() 则只阻止事件的进一步传播,不阻止事件本身。

拓展

stopImmediatePropagation 的使用

这玩意儿是 w3c 的东西,使用的也不是特别多,我们知道 stopPropagation 可以阻止事件的进一步传播,但是他阻止不了该元素上绑定的其他函数的执行,比如我们在 obj 上绑定了 func1 和 func2,如果我们在 func1 中使用了 stopPropagation ,那 func2 依然还是会执行出来。倘若这里使用 stopImmediatePropagation,结果就不一样了,他不仅阻止事件的传播,还阻止 func2 的执行。如:

结果是:

而改成evt.stopImmediatePropagation();之后,阻止了第二个监听事件的触发:

结果是:

setCapture 和 releaseCapture

这两个是 IE 下的事件绑定函数,只要我们在某个元素上 setCapture 了,那么你在任何地方的鼠标操作(mouseXXX之类的动作)都会在这个元素上触发(前提是你在这个元素上绑定了事件),releaseCapture 或者本窗口失去聚焦才会释放这个绑定~

参考文章

JavaScript 详说事件机制之冒泡、捕获、传播、委托[解惑]JavaScript事件机制

DOM事件传播机制 js中的事件委托或事件代理详解

click事件在什么时候出发_超全的js事件机制amp;事件委托相关推荐

  1. concat合并的数组会有顺序么_超全的JS常用数组方法整理

    前言 常用数组方法汇总 方法解析 1:concat(); 2:join(); 3:pop(); 4:shift(); 5:unshift(); 7:reverse(); 8:sort(); 9:sli ...

  2. click事件在什么时候出发_什么是移动端?

    主要内容什么是移动端.移动端视口.rem em px区别.移动端适配.移动端布局和马蜂窝案例. 结合视频学习效果更佳: https://www.bjsxt.com/down/9319.html​www ...

  3. click事件在什么时候出发_剖析setTimeout和click点击事件的触发顺序

    下面是一段非常简单的JavaScript代码 dianji setTimeout(function () { alert('timer handler') }, 2000) function test ...

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

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

  5. java web视频_超全面的JavaWeb视频教程

    超全面的JavaWeb视频教程 此套JavaWeb视频为非常全面的教程,适合JavaWeb爱好者自学.课程循序渐进.深入浅出,涉及到所有的知识点.学会本套教程,你就可以完全掌握到JavaWeb开发精髓 ...

  6. vant 半圆仪表盘_超全的 Vue 开源项目合集,签收一下

    原标题:超全的 Vue 开源项目合集,签收一下 写在前面 包括一些ui库和比较完整的小项目.ui库会对主要的单选框,多选框,级联选择器,滑块,日期/时间选择器,进度条,分页,弹框,通知,导航菜单,步骤 ...

  7. maya室内模型_超全室内外设计贴图素材库合辑 | 57G

    点击上方 蓝色  关注CG云分享 合辑ID : HJ 43775 Hello,今天为各位带来一套超全室内外设计贴图素材库合辑 本期资源--[超全室内外设计贴图素材库合辑],囊括了植被贴图.X射线贴图. ...

  8. 如何提取明细表头_超全!197页建筑工程预算实例教程+241页预算明细表,造价轻松算...

    对于一名优秀的造价人来说,算好建筑工程预算是做造价的第一步!也是至关重要的一个环节! 面对大量的图纸信息如何快速提取内容? 如何牢记定额,轻松算量? 怎么能够做到预算最低偏差? 那么这些就是考验造价员 ...

  9. 头的各个部位示意图_超全!27张高清解剖图带你认识头、面、颈部骨骼及肌肉名称!珍藏!...

    原标题:超全!27张高清解剖图带你认识头.面.颈部骨骼及肌肉名称!珍藏! 头部是身体的核心,作用相当于"司令部" 有以下值得注意的地方 第一 头部容纳脑,是身体的控制中心,一些人认 ...

最新文章

  1. vue如何配置服务器端跨域_客户端(vue框架)与服务器(koa框架)通信及服务器跨域配置详解...
  2. 经验总结 | 重构让你的代码更优美和简洁
  3. php和mysql实现模糊查询_PHP MYSQL实现登陆和模糊查询两大功能_PHP
  4. Cisco2811基本操作
  5. Linux启动/停止/重启Mysql数据库的方法
  6. SAP Cloud for Customer Price-计价简介 1
  7. java中i+=2什么意思_三分钟看懂Java中i++与++i的性能差别以及循环中如何使用
  8. altera fpga sdi输出方案_高段位攻城狮是这样解决SI分析、DDR、FPGA国产化问题的! | 电巢直播答疑汇总...
  9. ZooKeeper 到底解决了什么问题?
  10. OpenJudge计算概论-字符串最大跨距
  11. 学计算机的数学一定要好吗,学计算机一定要数学好吗?
  12. R语言基础作图之点图
  13. 前端H5新增标签和CSS3高级
  14. MINIO_ACCESS_KEY and MINIO_SECRET_KEY are deprecated,Please use MINIO_ROOT_USER and MINIO_ROOT_PASSW
  15. 二次吐血整理的 MAYA教程 快捷键大全,别收藏,直接粘贴拿走!
  16. 主DNS、辅助DNS、缓存DNS和基于CDN的利用DNS服务器实现负载均衡
  17. iview(View UI)使用 Vue 的 Render 函数,自定义表格列头显示内容(renderHeader)
  18. Vscode 使用 Code Runner 输出乱码,不只中文
  19. mysql学习总结-初识+数据管理+增删改查+常用函数+事务理解+数据库设计+JDBC引入
  20. JAVA cmd 命令运行 jar 包

热门文章

  1. Message popover
  2. S4 KNUMH的设计原理
  3. SAP WebIDE编辑器的主题设置
  4. SAP Marketing和SAP Marketing Cloud的区别
  5. why product overview page could not be displayed in QI2 506
  6. 如何启用SAP CRM text的html编辑器
  7. 如何解决error message Data cannot be maintained for set type COM_TA_R3_ID
  8. 最简单的Docker镜像教程:从头基于空镜像scratch创建一个新的Docker镜像
  9. 运行npm update等命令出错后如何分析问题根源
  10. 阿里云上到底能运行SAP哪些产品?