Javascript事件冒泡与事件委托
用户与网页发生交互的时候会发生事件,这些事件触发了以后并不会简单的结束,还会触发事件流。某个元素被触发事件的时候该元素处于文档之中,单个元素被触发之后处于文档中的其他元素也会被影响。
一、事件冒泡
当网页中的元素触发事件之后,会以冒泡的形式从底层向顶层传播
二、事件捕获
网页中的元素触发事件之后,目标元素不会马上被触发,而是由顶层向底层逐步捕获目标元素
三、DOM事件流
在DOM2中规定,事件流会有三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段

四、事件处理程序
事件处理程序是当事件触发时所要作出什么反应的程序函数,可以通过不同的方式进行事件绑定。(写好一个函数,当什么元素触发什么事件的时候将写好的函数给到对应的事件处理属性)。分为 HTML事件处理程序 、DOM0级事件处理程序 、 DOM2级事件处理程序 .
不同的事件处理程序使用的事件流顺序不同 ,HTML事件处理程序与DOM0级事件处理程序都是采用事件冒泡、DOM2可以指定使用哪种事件流处理方式(默认也是事件冒泡方式)
例如下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div class="grandmother" style="height: 200px; width: 200px; border: 2px solid black;">grandmother <div class="mother" style="height: 120px; width: 120px; border: 2px solid black;">mother <div class="son" style="height: 80px; width: 80px; border: 2px solid black;">son <div class="baby" style="height: 50px; width: 50px; border: 2px solid black;">baby</div> </div> </div> </div> <script> var grandmother = document.getElementsByClassName("grandmother")[0]; var mother = document.getElementsByClassName("mother")[0]; var son = document.getElementsByClassName("son")[0]; var baby = document.getElementsByClassName("baby")[0];
function sayName(){ console.log("I am " + this.className); } baby.addEventListener("click",sayName,false); son.addEventListener("click",sayName,true); mother.addEventListener("click",sayName,true); grandmother.onclick = sayName;
</script> </body> </html>
|

解释:上面的例子使用了不同的时间处理程序来执行,可以对比出事件捕获与事件冒泡的过程与优先级。从事件流的三个阶段来看,事件捕获是最先的,所以使输出son和mother这两个,而事件捕获是先从外层再到内层,所以先输出mother再输出son;之后到了事件冒泡,所以输出的是baby和grandmother,而事件冒泡是由底层再到外层,所以先输出baby再输出grandmother。
顺序是 先捕获(先外层再内层)再冒泡(先内层再外层)
五、事件委托
(一)、为什么要使用事件委托
要理解事件委托,首先要理解为什么要使用事件委托。因为Javascript在关于性能调优上的直观思想就是减少DOM操作,减少内存开销。比如下面这个例子:
当要实现点击li结点输出结点的文字时,常规思路是使用循环的方式为每个li结点绑定点击事件,然后再输出对应的文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <ul> <li>岛田庄司</li> <li>鲇川哲也</li> <li>柯南道尔</li> <li>阿加莎克里斯蒂</li> </ul> <script> var lis = document.querySelectorAll("li"); for(let i=0;i<lis.length;i++){ lis[i].onclick = function(){ console.log(lis[i].innerHTML); } } </script> </body> </html>
|

上面这种做法为每个li结点都绑定了点击事件,四个li对应着四个不同的function对象(Object类型),这种情况当然允许这样写代码,但是在li的数目变多的时候,这对页面性能的开销是十分巨大的。为了解决类似问题,引入了事件委托这种操作。
(二)、什么是事件委托及原理
事件委托的理解是 本来应该加在子元素身上的事件,我们却把事件加在了其父级身上 .委托父级元素统一处理某一类问题。
原理:理解事件委托涉及 事件冒泡 与 事件对象 的属性target。
有一个问题:子元素的事件添加到了父元素身上,那如何区分事件本来属于哪个子元素呢。这就要用到事件对象event 。事件对象记录着事件发生的所有信息,其中的event.target属性代表真实是哪个DOM结点触发了事件。
将原理整合起来的理解就是: 将各类子元素所涉及的事件利用事件冒泡统一委托给父级元素(集中管理),然后利用event.target定位具体是哪个子元素触发了事件,然后进行关于该子元素的需要触发对应的事件。
(三)、事件委托的实现
将上述的 li 例子使用事件委托的方式进行优化如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <ul> <li>岛田庄司</li> <li>鲇川哲也</li> <li>柯南道尔</li> <li>阿加莎克里斯蒂</li> </ul> <script> var ul = document.querySelector("ul"); ul.onclick = function(event){ console.log(event.target.innerHTML); } </script> </body> </html>
|
上面使用了事件委托的方式对代码进行了优化,实现同样的功能但是由原来的四个事件绑定减少为一个事件绑定。函数减少了对应的内存占用得到优化。
(四)、兼容性
Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom。这个是有兼容性的,标准浏览器用event.target,IE浏览器用event.srcElement.