DOM事件相关

70 阅读3分钟

事件委托

事件委托是 JavaScript 中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父元素,让父元素担当事件监听的职务。事件代理的原理是 DOM 元素的事件冒泡。

如下图所示,事件传播分成三个阶段:

  1. 当一个事件触发后,从 Window 对象触发,不断经过下级节点,直到目标节点。在事件到达目标节点之前的过程就是捕获阶段。所有经过的节点,都会触发对应的事件;
  2. 目标阶段:在目标节点上触发,称为“目标阶段”
  3. 冒泡阶段:从目标节点传导回 window 对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层;

假设有 body 和 body 节点下的 div1 均有绑定了一个注册事件:

  • 当为事件捕获(useCapture:true)时,先执行 body 的事件,再执行 div 的事件。
  • 当为事件冒泡(useCapture:false)时,先执行 div 的事件,再执行 body 的事件。
  1. 当 useCapture 为默认 false 时,为事件冒泡

    <body>
        <div id="div1"></div>
    </body>
    
    window.onload = function(){
        let body = document.querySelector('body');
        let div1 = document.getElementById('div1');
        body.addEventListener('click',function(){
            console.log('打印body')
        })
        div1.addEventListener('click',function(){
            console.log('打印div1')
        })
    }
    
    //结果:打印div1  打印body
    
  2. 当 useCapture 为 true 时,为事件捕获

    <body>
        <div id="div1"></div>
    </body>
    
    window.onload = function(){
        let body = document.querySelector('body');
        let div1 = document.getElementById('div1');
        body.addEventListener('click',function(){
            console.log('打印body')
        },true)
        div1.addEventListener('click',function(){
            console.log('打印div1')
        })
    }
    //结果:打印 body 打印 div1
    

事件委托的优点

  1. 提高性能:每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。
  2. 动态监听:使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。
<script>
    window.onload = function(){
        let div = document.getElementById('div');

        div.addEventListener('click',function(e){
            console.log(e.target)
        })

        let div3 = document.createElement('div');
        div3.setAttribute('class','div3')
        div3.innerHTML = 'div3';
        div.appendChild(div3)
    }
</script>


<body>
    <div id="div">
        <div class="div1">div1</div>
        <div class="div2">div2</div>
    </div>
</body>

虽然没有给 div1 和 div2 添加点击事件,但是无论是点击 div1 还是 div2,都会打印当前节点。因为其父级绑定了点击事件,点击 div1 后冒泡上去的时候,执行父级的事件。

阻止默认事件

有一些 html 元素默认的行为,比如说 a 标签,点击后有跳转动作;form 表单中的 submit 类型的 input 有一个默认提交跳转事件;reset 类型的 input 有重置表单行为。

如果你想阻止这些浏览器默认行为,JavaScript 为你提供了方法。

 var a = document.getElementsByTagName("a")[0];
 a.onclick = function(e){
     e.preventDefault();
     //return false;//也可以
 }

默认事件没有了。仅仅是在 HTML 事件属性 和 DOM 0 级事件处理方法中 才能通过返回 return false 的形式阻止事件宿主的默认行为。DOM0 事件模型,所有的浏览器都支持。直接在 dom 对象上注册事件名称,就是 DOM0 写法.

阻止事件冒泡

事件触发时,会默认传入一个 event 对象,这个 event 对象上有一个方法:stopPropagation,通过此方法,可以阻止冒泡,这样外层 dom 就接收不到事件了。代码如下:

var btn = document.getElementById("test");
var btnInner = document.getElementById("testInner");

btn.addEventListener("click", function(e){
  console.log("div");
}, false);

btnInner.addEventListener("click", function(e){
  //阻止冒泡
  e.stopPropagation();
  console.log("内层div");
}, false);