DOM事件绑定详解

70 阅读2分钟

之前学习对DOM事件没有太注意,只知道 addEventListener 绑定,stopPropagation 停止冒泡,preventDefault 阻止默认事件。但对于更为细节的就不太清楚,于是便有了这篇笔记

绑定事件的方式

三种

/*
*   1
*   一个元素绑定两个处理函数,会覆盖
*/ 
elem.onclick = function(){}
​
/*
*   2
*   一个元素绑定两个处理函数,会覆盖
*   false事件冒泡  true事件捕获
*   ie9 以下不兼容,W3C 规范
*   同一个元素可以绑定多个事件
*/ 
elem.addEventListener(事件类型,事件处理函数,false)
​
/*
*   3
*   ie8 及以下
*   this` 指向 `window
*/ 
elem.attachEvent('onclick',function(){
    test.call(elem);
})
function test(){} // 解决attachEvent 中this指向问题
​

解除绑定的方式

两种

elem.onclick = null / false
​
elem.removeEventListener() // 参数需要和绑定时候的参数完全相同
elem.addEventListener('click', function(){
    this.className = '';
  this.innerHTML = '';
  this.removeEventListenner('click', arguments.callee, false); // 非严格模式下
})

捕获和冒泡

focus blur change submit reset select** **没有冒泡

怎么判断

elem.addEventListener(事件类型,事件处理函数,false) // false 代表冒泡   true 代表捕获

冒泡

事件开始时由最具体的元素接收 然后逐级向上传播到较为不具体的节点

捕获 不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件

DOM 事件流

事件流描述的是从页面中接受事件的顺序 业内有两种事件流的概念,分别是IE的冒泡流、网景的捕获流 IE:事件是由具体的DOM节点应该最早接收到事件,而最不具体的节点应该最后接收到事件 网景:事件开始时由最不具体的元素接收,然后逐级向下传播到具体的节点

冒泡流

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body onclick="bodyClick()">
    <div onclick="divClick()">
      <button onclick="btn()">
        <p onclick="p()">点击冒泡</p>
      </button>
    </div>
    <script>
      function p() {
        console.log("p标签被点击");
      }
      function btn() {
        console.log("button被点击");
      }
      function divClick(event) {
        console.log("div被点击");
      }
      function bodyClick() {
        console.log("body被点击");
      }
    </script>
  </body>
</html>
​
/*
p标签被点击
button被点击
div被点击
body被点击
*/

捕获流

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style>
      .wrapper {
        width: 150px;
        height: 150px;
        background-color: aqua;
      }
​
      .outer {
        width: 100px;
        height: 100px;
        margin-left: 150px;
        background-color: bisque;
      }
​
      .inner {
        width: 75px;
        height: 75px;
        margin-left: 100px;
        background-color: blueviolet;
      }
    </style>
  </head>
​
  <body>
    <div class="wrapper">
      <div class="outer">
        <div class="inner"></div>
      </div>
    </div>
    <script>
      var wrapper = document.getElementsByClassName("wrapper")[0];
      var outer = document.getElementsByClassName("outer")[0];
      var inner = document.getElementsByClassName("inner")[0];
​
      wrapper.addEventListener(
        "click",
        function () {
          console.log("wrapper-捕获");
        },
        true
      );
​
      outer.addEventListener(
        "click",
        function () {
          console.log("outer-捕获");
        },
        true
      );
      inner.addEventListener(
        "click",
        function () {
          console.log("inner-捕获");
        },
        true
      );
    </script>
  </body>
</html>
​
​
/*
wrapper-捕获
outer-捕获
inner-捕获
*/

两种流同时存在

先执行捕获流、在执行冒泡流

  • wrapper-捕获
  • outer-捕获
  • inner-捕获
  • wrapper-冒泡
  • outer-冒泡
  • inner-冒泡
<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style>
        .wrapper {
            width: 150px;
            height: 150px;
            background-color: aqua;
        }
​
        .outer {
            width: 100px;
            height: 100px;
            margin-left: 150px;
            background-color: bisque;
        }
​
        .inner {
            width: 75px;
            height: 75px;
            margin-left: 100px;
            background-color: blueviolet;
        }
    </style></head><body>
    <div class="wrapper">
        <div class="outer">
            <div class="inner"></div>
        </div>
    </div>
    <script>
        var wrapper = document.getElementsByClassName('wrapper')[0];
        var outer = document.getElementsByClassName('outer')[0];
        var inner = document.getElementsByClassName('inner')[0];
       
​
​
        wrapper.addEventListener('click', function () {
            console.log('wrapper-捕获');
        }, true);
​
        outer.addEventListener('click', function () {
            console.log('outer-捕获');
        }, true);
        inner.addEventListener('click', function () {
            console.log('inner-捕获');
        }, true);
​
        wrapper.addEventListener('click', function () {
            console.log('wrapper-冒泡');
        }, false);
        outer.addEventListener('click', function () {
            console.log('outer-冒泡');
        }, false);
​
        inner.addEventListener('click', function () {
            console.log('inner-冒泡');
        }, false);
    </script>
</body></html>

当冒泡捕获同时存在时,也会先绑定捕获,在绑定冒泡,于是先执行捕获、在执行冒泡,这是由于DOM 2级事件规定

DOM 2级事件

规定的事件流包含3个阶段,事件捕获阶段、处于目标阶段、事件冒泡阶段。这个意思就是DOM事件触发了(用onclick触发),先捕获,看看有没有父级DOM有相同的事件,DOM父级执行完后,到了目标阶段,然后再进行冒泡 冒泡捕获图.jpg

总结

DOM事件的执行与捕获冒泡无关,真正影响其执行顺序的是绑定顺序,绑定的最早,最早执行; 绑定是由捕获流和冒泡流决定的,onClick方式绑定默认是冒泡流,只有 addEventListener设置为true才会捕获流

当冒泡捕获同时存在时,也会先绑定捕获,在绑定冒泡,于是先执行捕获、在执行冒泡