浏览器的事件委托详解
了解事件机制首先要了解事件和事件流。
1. 事件处理程序
事件处理程序分为3类:
- HTML事件处理程序
- DOM0级事件处理程序
- DOM2级事件处理程序
1. HTML事件处理程序
<button onclick="alert(hello world!)"></hello>
特点是:
HTML和JS高度耦合;当需要修改函数名称时,需要同时修改两个地方
2. DOM0级事件处理程序
DOM0级事件处理程序指的是通过给DOM节点通过JS添加on[event]事件。
var btn=document.getElementById("#btn"); btn.onclick=function(){ alert(hello world!) }
特点是:
每个DOM只能添加一个事件;
3. DOM2级事件处理程序
其特点:
可以给DOM节点同时添加多个处理事件
IE10-对应的方法是:attachEvent/detachEvent,用法如下:
function handleEvent(event) { // TODO } dom.attachEvent(eventName, handleEvent) // attachEvent只能在冒泡阶段和目标阶段触发 dom.detachEvent(eventName, handleEvent)
其事件对象的获取可以直接在监听函数的参数中获取,其属性有:
1. type - 获取事件类型 2. srcElement - 获取目标对象 3. cancelBubble = true 阻止事件冒泡 4. returnValue = false 阻止事件的默认行为
IE11+及其他浏览器对应的方法是addEventListener/removeEventListener。其用法如下:
function handleEvent(event) { // TODO } dom.addEventListener(eventName, handleEvent , false); // 即{useCapture: false},可在冒泡阶段/目标阶段触发;默认值 dom.removeEventListener(eventName, handleEvent, false);
其对应的事件对象的属性有:
1. type - 获取事件类型 2. target - 获取事件目标 3. stopPropagation() - 阻止事件传播 4. preventDefault() - 阻止事件的默认行为
2. 事件流
事件流是在页面中接受事件的顺序, 即事件传播的顺序。事件传播包含三个阶段:
- 捕获阶段-从最外层->目标DOM
- 目标阶段
- 冒泡阶段-目标DOM->最外层
当冒泡和捕获同时存在时,事件触发顺序为:捕获->目标->冒泡
示例:
<!-- 单击son--> <!DOCTYPE html> <html lang="en"> <body> <div id="father" style="width: 400px;height: 200px;background-color: bisque"> <div id="son" style="width: 200px; height: 100px;background-color: brown"></div> </div> <script> document.body.addEventListener('click', function(event) { console.log('body-bubble') }, false); // 注意不能直接将parent作为id,会代表window.parent father.addEventListener('click', function(event) { console.log('father-bubble'); }, false); document.body.addEventListener('click', function(event) { console.log('body-capture') }, true); father.addEventListener('click', function(event) { console.log('father-capture'); }, true); son.addEventListener('click', function(event) { console.log('son'); }, false); </script> </body> </html>
运行结果
body-capture father-capture son father-bubble body-bubble
3. 事件委托
事件委托,也叫事件代理。
1. 事件委托的原理
一般指的是利用事件冒泡,将一个或者一组DOM的事件处理程序,委托给其父级元素或者更外层元素。
2. 事件委托的意义
可以通过只指定一个事件处理程序,就可以管理一批同类型的事件处理程序。
如果需要给列表项添加事件,当列表项数量过多时,会存在频繁操作DOM的情况;并且函数是引用类型, 需要堆内存来存储,占用大量空间。通过事件委托,可以将事件绑定到其父级元素ul上,这样只需要操作一次DOM,不会引起频繁的重绘。也不会占用大量的空间。
示例:
<!-- 通过事件的具体属性实现通过事件委托也和直接绑定在li上一样的效果--><!DOCTYPE html> <html lang="en"> <body> <ul id='father'> <li id="a">A</li> <li id="b">B</li> <li id="c">C</li> <li id="d">D</li> </ul> <script> father.addEventListener('click', function(e) { const event = e || window.event; const target = event.target || event.srcElement; // 根据事件属性判断当前所在的DOM节点,不同节点处理不同的程序 switch(target.id) { case "a": console.log('a'); break; case "b": console.log('b'); break; case "c": console.log("c"); break; case "d": console.log("d"); break; default: console.log('default'); } }) </script> </body> </html>