事件冒泡、事件捕获、事件委托

116 阅读2分钟

简介

事件冒泡和事件捕获是由微软和网景公司提出,这两个概念是为了解决页面中事件流(事件发生顺序)的问题。

假设三个div标签呈嵌套关系且这三个div上分别注册了相同的click事件。那么他们的触发顺序是怎样的呢?为了解决这个问题,微软和网景提出了分别提出了事件冒泡和事件捕获,两种几乎相反的概念。

image.png

  <div id="test1">
        test1
        <div id="test2">
            test2
            <div id="test3">test3</div>
        </div>
    </div>


        const divs = document.getElementsByTagName('div');
        console.log('divs', divs);
        //divs是伪数组,需要转换成真正的数组 console.log([...divs] instanceof Array); false

        var flag = false; //事件冒泡
        // var flag = true; //事件捕获
        [...divs].forEach(item => {
            item.addEventListener('click', f1, flag);
        })

       function f1(e) {
            // e.stopPropagation(); //阻止事件冒泡和事件捕获
            // e.cancelBubble = true;  //不推荐使用 已经被移除web标准
            console.log(this.id);
        }

事件冒泡(event bubbling)

事件会从最内层的元素开始发生,一直向上传播,直到document对象。

点击最里层的黄色test3,控制台打印出:

image.png

事件捕获(event capturing)

与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

点击最里层的黄色test3,控制台打印出:

image.png

事件委托

事件委托是利用事件冒泡的原理。

一个简单的小例子,可以清楚的说明事件委托:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

  <script>
        const LiList = document.getElementsByTagName('li');
        [...LiList].forEach(li=>{
            li.onclick = function(){
                console.log(li.innerHTML);
            }
        })
   </script>

代码中有3个li,每一个li上都注册了一个函数,那么这3个函数会被创建在内存中占用空间。这是对内存的浪费!

解决办法: 可以利用事件冒泡的原理,把事件注册在父元素ul上。 点击某个具体元素,会依次一级一级往上调用父级元素的同名事件,直到window.

代码:

const ul = document.getElementsByTagName('ul')[0];

ul.onclick = function(e){
      e = e || window.event;
      console.log(e.target); //li  实际点击的具体元素li
      console.log(e.currentTarget); //ul   绑定事件的元素ul
      if(e.target.nodeName.toLowerCase()==='li'){
         console.log(e.target.innerHTML);
       }
 }

利用事件冒泡的原理,把事件注册在父元素上,这种处理方法叫做事件委托。 事件委托还有一个好处就是:当新增li时,不必在新增li上注册事件。