什么是事件流呢?本文主要和大家分享js事件流以及扩展应用实例,希望能帮助到大家。
DOM标准规定事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
● 事件捕获阶段:实际目标(e388a4556c0f65e1904146cc1a846bee
)在捕获阶段不会接收事件。也就是在捕获阶段,事件从document到100db36a723c770d327fc0aef2ce13b1
再到6c04bd5ca3fcae76e30b72ad730ca86d
就停止了。上图中为1~3.
● 处于目标阶段:事件在e388a4556c0f65e1904146cc1a846bee
上发生并处理。但是事件处理会被看成是冒泡阶段的一部分。
● 冒泡阶段:事件又传播回文档。
note:
1)、尽管“DOM2级事件”标准规范明确规定事件捕获阶段不会涉及事件目标,但是在IE9、Safari、Chrome、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两次机会在目标对象上面操作事件。
2)、并非所有的事件都会经过冒泡阶段 。所有的事件都要经过捕获阶段和处于目标阶段,但是有些事件会跳过冒泡阶段:如,获得输入焦点的focus事件和失去输入焦点的blur事件。
捕获型事件流:事件的传播是从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子。
冒泡型事件流:事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。
那么这里要实现的就是冒泡型事件流,从里面(叶子)到外面(根)
//只需要在window.onload里面给每一个圆添加点击事件处理程序就ok了哦,其余代码请看上[一篇文章](https://blog.csdn.net/weixin_38323736/article/details/79685589)当然是在圆画好之后执行这一段代码,所以位置放在后面,别放错var circles=document.getElementsByClassName("circle"); for(var i=0;i<n;i++){ circles[i].onclick=function(e){ //currentTarget表示当前处理该事件的元素、文档或窗口,childNodes是子节点的意思,这里遍历子节点 e.currentTarget.childNodes.forEach(function(v) { //把文本节点找出,不然html代码也会输出的 if(v instanceof Text) { //文字节点为Text()实例,用data或者wholeText可以取到String类型的文本 //解决方法参照:https://segmentfault.com/q/1010000009913772/a-1020000009914008 console.log(v.data); // console.log(v.wholeText); } }); } }
ok啦,就是这么简单。
好学的同学肯定想看看事件捕获了,干脆我把两种都写出来吧,让大家看看整个事件流是怎么样的
circles[i].onclick=function(e){}
这种写法是dom0级的写法,只能写一个事件,再写一个会覆盖,而且只支持冒泡事件 addEventListener
则不一样,它可以写多个事件,不会覆盖
//这段代码包含了上面的js代码哦var circles=document.getElementsByClassName("circle");for(var i=0;i<n;i++){ circles[i].addEventListener("click",function(e){ e.currentTarget.childNodes.forEach(function(v) { if(v instanceof Text) { console.log(v.data+" 捕获阶段"); } }); //true表示事件句柄在捕获阶段执行; //false- 默认。事件句柄在冒泡阶段执行 },true); circles[i].addEventListener("click",function(e){ e.currentTarget.childNodes.forEach(function(v) { if(v instanceof Text) { console.log(v.data+" 冒泡阶段"); } }); },false); }
这时候,点击15,就会出现下面的效果啦
ok啦,其实事件冒泡有一个很好的应用哦,就是事件代理
事件代理也即事件委托
传统的事件处理中,需要为每个元素添加事件处理器。js事件代理则是一种简单有效的技巧,通过它可以把事件处理器添加到一个父级元素上,从而避免把事件处理器添加到多个子级元素上。
事件代理的原理用到的就是事件冒泡和目标元素,把事件处理器添加到父元素,等待子元素事件冒泡,并且父元素能够通过target(IE为srcElement)判断是哪个子元素,从而做相应处理。
事件代理的好处
● 将多个事件处理器减少到一个,因为事件处理器要驻留内存,这样就提高了性能。想象如果有一个100行的表格,对比传统的为每个单元格绑定事件处理器的方式和事件代理(即table上添加一个事件处理器),不难得出结论,事件代理确实避免了一些潜在的风险,提高了性能。
● DOM更新无需重新绑定事件处理器,因为事件代理对不同子元素可采用不同处理方法。如果新增其他子元素(a,span,p等),直接修改事件代理的事件处理函数即可,不需要重新绑定处理器,不需要再次循环遍历。
比如,现在我想,点击15的圆的时候,就输出15,我们先用传统循环的方法来写,再用事件委托的方法来写
var circles=document.getElementsByClassName("circle");for(var i=0;i<n;i++){ circles[i].addEventListener("click",function(e){ e.currentTarget.childNodes.forEach(function(v) { if(v instanceof Text) { console.log(v.data); } }); //阻止冒泡哦!!!不然不止输出15了 e.stopPropagation(); },false); }
这种方法其实是耗性能的,循环几次,编译时就会写几次,所以还是不如事件代理,只需要写一次:
//获取外面的大圆,只需要交给大圆来处理就okvar circle=document.getElementById("circle"); circle.addEventListener("click",function(e){ e=e||window.event; var targetElement=e.target||e.srcElement; targetElement.childNodes.forEach(function(v) { if(v instanceof Text) { console.log(v.data); } }); })
哈哈,有没有人想问,那上面用了那么多循环,怎么不也用事件代理写了呢?我想了一下,还真可以写哦
var circle=document.getElementById("circle"); circle.addEventListener("click",function(e){ e=e||window.event; var targetElement=e.target||e.srcElement; while(targetElement.nodeName!="BODY"){ targetElement.childNodes.forEach(function(v) { if(v instanceof Text) { console.log(v.data); } }); targetElement=targetElement.parentNode; } },false)
相关推荐:
以上是js事件流以及扩展应用实例的详细内容。更多信息请关注PHP中文网其他相关文章!