DOM事件流的小总结

2,277 阅读4分钟

废话不多说,直接开始我的笔记吧~

DOM事件级别

DOM级别一共有四个级别,分为: DOM0级、DOM1级、DOM2级以及DOM3级。

DOM事件分为三个级别: DOM0级事件处理、DOM2级事件处理、DOM3级事件处理。

那为什么没有DOM1级事件处理呢? 因为DOM1级里面没有相关定义。 别问我怎么知道的,因为我也只知道这么多~

DOM0级事件

element.onclick = function(){}

话不多说,先上代码

 <button id="btn">点击</button>
  
  <script>
    const btn = document.getElementById('btn')

    btn.onclick = () => {
      alert('你好')
    }
    
    // btn.onclick = null用来解绑
  </script>

HTML事件处理程序中,onclick事件绑定在button标签中,这样的话如果修改函数名就需要修改至少两处,代码量大了,也就不方便修改。

如上代码中为DOM0级事件处理,通过操作DOM来完成事件的绑定。

那么什么是DOM0级处理事件呢? 它就是将一个函数赋值给事件处理属性,如上面栗子中js通过获取id按钮,将函数赋值给onclick这个处理事件。

DOM0级事件的缺点在于它不能同时绑定多个相同事件,如果绑定多个,则最新绑定的这个事件会覆盖前面一个,在这里我就不多展示了,感兴趣的可以自己试试。

DOM2级事件

element.addEventListener('click',function(){},false)

<button id="btn">点击</button>

  <script>
    const btn = document.getElementById('btn')

    btn.addEventListener('click', () => {
      alert('你好')
    }, false)
    btn.addEventListener('click', () => {
      alert('我不好')
    }, false)
    
    // btn.removeEventListener('click', () => {}, false); 解绑事件
  </script>

如代码所使,DOM2级事解决了在DOM0级不能同时绑定多个相同处理事件的缺点,如代码所示,点击按钮后,会按照代码顺序,先执行你好,再执行我不好

DOM2级事件中处理函数包含了三个参数: 绑定事件处理属性名称(不需要加on)、处理函数、是否是事件捕获

IE8以下的版本不支持addEventListener,需要用attachEvent并且不需要第三个参数,因为IE8以下版本只支持冒泡事件

DOM3级事件

element.addEventListener('keyup',function(){},false)

DOM3级事件就是在DOM2基础上增加了一些事件类型,如滚动,键盘等事件。同时,DOM3级事件也允许使用者自定义一些事件。

DOM事件流

事件处理程序

从上文中我们可以看到事件处理程序就是例如onclick,onload等,DOM2中也提供了两个处理和删除事件处理程序的操作:addEventListener()removeEventListener()能够全局访问任何代码块。

详细内容在前文已经讲过,就不过多解释了。

DOM事件模型

事件冒泡: 由最具体的元素(例如按钮)逐级到较为不具体的元素(如document),简单来说就是由里到外。

事件委托: 就是利用了事件冒泡机制,自己触发的事件,让父元素代替执行;这样大量减少内存占用,增加效率。例如卡片式点击。

<div id="parent">
    <button id="son">事件冒泡</button>
  </div>

  <script>
    const parent = document.getElementById('parent')
    const son = document.getElementById('son')

    parent.addEventListener('click', () => {
      console.log('我是父亲')
    }, false)

    son.addEventListener('click', () => {
      console.log('我是孩子')
    }, false)
  </script>
  <!--
  结果: 
  我是孩子
  我是父亲
  -->

事件捕获: 与事件冒泡的顺序相反,从window开始捕获。就是有外到里。

<div id="parent">
    <button id="son">事件捕获</button>
  </div>

  <script>
    const parent = document.getElementById('parent')
    const son = document.getElementById('son')

    parent.addEventListener('click', () => {
      console.log('我是父亲')
    }, true)

    son.addEventListener('click', () => {
      console.log('我是孩子')
    }, true)
  </script>
  <!--
  结果: 
  我是父亲
  我是孩子
  -->

看张图片可能更加清晰吧~

该图来源于网络

DOM事件流

同时支持两种事件模型: 事件捕获事件冒泡,他们执行的顺序为:

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段

这个前提是捕获阶段不会涉及事件目标。但是有些浏览器在捕获阶段会涉及事件目标

来看个栗子吧~

<div id="parent">
    我是父亲
    <div id="son">
      我是孩子
    </div>
  </div>

  <script>
    const parent = document.getElementById('parent')
    const son = document.getElementById('son')

    // 父亲捕获
    parent.addEventListener('click', () => {
      console.log('父亲捕获')
    }, true)
    // 父亲冒泡
    parent.addEventListener('click', () => {
      console.log('父亲冒泡')
    }, false)
    // 孩子捕获
    son.addEventListener('click', () => {
      console.log('孩子捕获')
    }, true)
    // 孩子冒泡
    son.addEventListener('click', () => {
      console.log('孩子冒泡')
    }, false)
  </script>

大家觉得结果是什么呢?

答案应该先父亲捕获,紧接着到目标程序,孩子捕获孩子冒泡,然后在进行父亲冒泡

那如果我将目标程序中代码顺序改变下,结果还是一样的么

<div id="parent">
    我是父亲
    <div id="son">
      我是孩子
    </div>
  </div>

  <script>
    const parent = document.getElementById('parent')
    const son = document.getElementById('son')
    //对父亲也进行顺序修改
    // 父亲冒泡
    parent.addEventListener('click', () => {
      console.log('父亲冒泡')
    }, false)
    // 父亲捕获
    parent.addEventListener('click', () => {
      console.log('父亲捕获')
    }, true)
    
    //在这里修改顺序
    // 孩子冒泡
    son.addEventListener('click', () => {
      console.log('孩子冒泡')
    }, false)
    // 孩子捕获
    son.addEventListener('click', () => {
      console.log('孩子捕获')
    }, true)
  </script>

大家看看我的截图吧

显而易见,答案与之前的不一样了,不是说好先进行事件捕获再进行事件冒泡的么?怎么改一下代码顺序就不一样了呢, 而且奇怪的是,父亲代码顺序修改不会对结果有影响(前提是点击我是孩子时,点击我是父亲也会改变),而孩子代码修改却让结果不一样了。

这里面的神奇之处是因为孩子代码是目标程序,目标程序是不会对事件流中的事先定好的排位顺序唯命是从了, 他们更加公平, 遵守的是先来后到的规则, 无论是事件捕获还是事件冒泡, 只要谁先出现在代码里,谁就先执行。

注:此文为本人学习过程中的笔记记录,如果有错误或者不准确的地方请大佬多多指教~