DOM事件和事件委托

228 阅读3分钟

1. DOM事件模型

DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。DOM的方法(methods)让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行。

捕获和冒泡:

Netscape认为outer上的处理函数应该先被执行. 这被称作event capturing,即从外到内

IE则认为inner上的处理函数具有执行优先权. 这被叫做event bubbling,即从内到外

于是W3C标准则取其折中方案. W3C事件模型中发生的任何事件, 先(从其祖先元素document)开始一路向下捕获, 直到达到目标元素, 其后再次从目标元素开始冒泡.(先捕获再冒泡,搞清楚这一点很重要)

开发者可以决定事件处理器是注册在捕获或者是冒泡阶段. 如果addEventListener的最后一个参数是true, 那么处理函数将在捕获阶段被触发; 否则(false), 会在冒泡阶段被触发.

也就是每点击一个元素,事件流都会像水泛涟漪一样,只是与涟漪正好相反,dom事件是以点击的元素为中心,先聚拢,再荡漾开去,false和true可以看成是标记,false或者不加参数表示注册在冒泡阶段执行,true表示注册在捕获阶段执行,

示例:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body>
 <div id="grand1">
   爷爷
   <div id="parent1">
     爸爸
     <div id="child1">
       儿子
     </div>
   </div>
 </div>
</body>
</html>
grand1.addEventListener('click',function f1(){
  console.log('爷爷')
},false)//冒泡

parent1.addEventListener('click',function f1(){
  console.log('爸爸')
},true)//捕获

child1.addEventListener('click',function f3(){
  console.log('儿子冒泡')
},false)//冒泡

child1.addEventListener('click',function f3(){
  console.log('儿子捕获')
},true)//捕获

结果依次为:

  1. 点击儿子
  2. 点击爸爸
  3. 点击爷爷

2. 事件委托

事件委托,通俗地来讲,就是把一个元素响应事件(click、keydown......)的函数委托到另一个元素,好处是节省了内存还能够动态监听元素

一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

示例:

   <div id="test">
        <div class="red">hi</div>
        <div class="red">hi</div>
        <div class="red">hi</div>
        <div class="red">hi</div>
        <div class="red">hi</div>
    </div>

 delegateEvent(parentSelector, targetSelector, events, fn) {
        // 事件处理逻辑,
        parentSelector.addEventListener(parentSelector, events, function (e) {

            let targetEl = e.target
            const currentTarget = e.currentTarget;

            // 遍历并判断是否为目标元素,如果不是,则往元素的 parentNode 继续查找
            while (!targetEl.matches(targetSelector)) {
                // 如果是目标元素则跳出循环
                if (targetEl === currentTarget) {
                    targetEl = null;
                    break;
                }
                targetEl = targetEl.parentNode;
            }

            if (targetEl) {
                // 将回调函数的 this 指向目标元素
                fn.call(targetEl, Array.prototype.slice.call(arguments));
            }
        });

    }
const test=document.querySelector('#test')
dom.delegateEvent(test,'#test .red','click',()=>{
    console.log('我被点击了')
})

最后发现了几篇相当详细的dom和事件委托的文章

DOM事件全整理之从DOM事件级别,DOM事件流到事件委托

JavaScript 事件委托详解