js 事件捕获和事件冒泡
先上一段超文本标记语言和内嵌层叠样式表:
<!DOCTYPE html> <html> <head> <title></title> <style type="text/css"> *{ margin:0; padding:0; text-align: center; } .el1{ height: 100px; background-color: red; } .el2{ height: 80px; background-color: cyan; } .el3{ height: 60px; background-color: gray; } </style> </head> <body> <div class="el1">el1 <div class="el2">el2 <div class="el3">el3</div> </div> </div> </body> </html>
复制到自己的ide然后用浏览器打开会看到这个页面:
现在给他们都绑定上一个事件监听函数:
//向下(子元素方向)捕获,向上(父元素方向)冒泡 //第三个参数设为true时,在事件捕获过程中被执行,为false时,在事件冒泡时被执行 window.onload = function() { el1 = document.getElementsByClassName('el1')[0] el2 = document.getElementsByClassName('el2')[0] el3 = document.getElementsByClassName('el3')[0] //执行顺序为:el2,el3,el1,因为el2和el3在添加事件监听时第三个参数为true,在事件捕获过程中执行了监听事件,可以看做true让监听事件优先执行,因为先进行捕获再进行冒泡 el1.addEventListener('click', domClick, false) el2.addEventListener('click', domClick, false) el3.addEventListener('click', domClick, false) function domClick(event) { console.log(this.className) } }
当我们点击el3时,会输出:
在w3c模型中,addEventListener方法有第三个参数来决定该方法是在哪个阶段被执行的,在触发事件时,会先进行事件的捕获,也就是从最外层开始,依次向点击的dom进行触发事件(前提是dom绑定了该事件类型的事件处理函数),到达点击的dom并触发该dom的监听事件后,再依次向外层触发事件,直到document对象,这个就是常说的向下捕捉,向上冒泡的事件触发过程,记住顺序就可以了,从父节点到子节点,再从子节点到父节点,先捕捉,再冒泡。
可以把这个过程想象成一个‘U‘,从左侧进入,向下捕捉,到达最底部,想让冒泡,事件完成,而对于addEventListener这个方法的第三个参数,true就带代表,这次绑定的方法是在事件捕获过程中要执行的,体现在‘U’上就是绑定到了左边,在事件触发过程中有优先执行的权利,相反,第三个参数设为false(默认为false)就是挂到了右边,会晚一些执行,现在验证这个想法是否正确,再为这三个dom绑定上方法2:
el1.addEventListener('click', domClick2, true) el2.addEventListener('click', domClick2, true) el3.addEventListener('click', domClick2, true) function domClick2(event) { console.log(this.className + "catch") }
刷新页面,点击el3,输出:
可以看到第三个参数为true的先执行了,至于第三个和第四个输出,原因是在w3c模型中,addEventListener方法绑定的函数,在执行时会按照绑定的顺序来触发的,我们先绑定了domClick,再绑定了domClick2
可以看到一个事件过程中,可以触发多个绑定的事件,被绑定的事件的执行顺序是由绑定的顺序和第三个参数来决定的。
当我们不想让事件进行冒泡的过程,只需要在方法的最底部添加:
event.stopPropagation()
针对这个功能,需要再改一下代码,这次将绑定事件的顺序调整一下,将方法区分开来,看的比较清晰一些:
window.onload = function() { el1 = document.getElementsByClassName('el1')[0] el2 = document.getElementsByClassName('el2')[0] el3 = document.getElementsByClassName('el3')[0] el1.addEventListener('click', el1Click, true) el2.addEventListener('click', el2Click, true) el3.addEventListener('click', el3Click, true) function el1Click(event) { console.log(this.className + "catch") // event.stopPropagation() } function el2Click(event) { console.log(this.className + "catch") // event.stopPropagation() } function el3Click(event) { console.log(this.className + "catch") // event.stopPropagation() } el1.addEventListener('click', el1Click2, false) el2.addEventListener('click', el2Click2, false) el3.addEventListener('click', el3Click2, false) function el1Click2(event) { console.log(this.className) // event.stopPropagation() } function el2Click2(event) { console.log(this.className) // event.stopPropagation() } function el3Click2(event) { console.log(this.className) // event.stopPropagation() } }
测试一下输出:
意料之中,对吧?
接下来,分别将这六个注释的代码取消注释
取消第一个的注释,点击el3,输出结果:
恩?事件捕获也终止在第一个方法了,看来不只是停止了冒泡,我们再验证一下
取消第二个的注释,注释第一条,点击el3,输出结果:
看来真的是这样
取消第五个注释,注释第二条,点击el3,输出结果:
就是在这个‘U’上面,哪个方法后面加了event.stopPropagation(),就会将本次事件停止到该方法。