关于JS事件冒泡与JS事件代理(事件委托)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SuperCoooooder/article/details/52190100

    事件作为DOM操作重要的一环,需要大家好好理解和运用,今天特意看了一下事件冒泡和事件代理的相关资料,感触颇深,也深感自己的无知不知道多浪费了多少内存,废话不多说进入正题:

1.事件冒泡:

      通俗易懂的来讲,就是当一个子元素的事件被触发的时候(如onclick事件),该事件会从事件源(被点击的子元素)开始逐级向上传播,触发父级元素的点击事件。下面见详细的代码:

     

 <div id="parent" style="background-color: #000;height: 400px;width: 400px" data-id="444">
        <div id="child" style="background-color: #fff;height: 200px;width: 200px" data-id="555"></div>
  </div>
document.getElementById('parent').onclick=function () {
        console.log(this.getAttribute('data-id'));
    };
document.getElementById('child').onclick=function () {
        console.log(this.getAttribute('data-id'));
    };

我们可以发现,当点击白色区域(子div)的时候,父级元素的click事件也被触发了。


看到这里不知有没有人跟我有同样的想法:

            1.要是不给子元素添加具体的oncilck处理方法,也能冒泡么?

            2.子元素触发的事件冒泡会触发父元素所有的事件么?还是触发对应的事件?

光说不练假把式,接下来就用代码探究一下:

1.

    document.getElementById('parent').onclick=function () {
        console.log(this.getAttribute('data-id'));
    };
//    document.getElementById('child').onclick=function () {
//        console.log(this.getAttribute('data-id'));
//    };

 

发现子元素在没有定义具体的click处理函数的时候仍然可以冒泡,触发父级元素的click事件。

2.

    document.getElementById('parent').onkeydown=function () {
        console.log(this.getAttribute('data-id'));
    };
    document.getElementById('child').onclick=function () {
        console.log(this.getAttribute('data-id'));
    };


我们发现只有相应的事件会发生事件冒泡,不相关的事件不受影响(注意由于click为鼠标的点击,所以同样会触发mousedown与mouseup等相关事件,同时发生冒泡!)


那么我们应该如何组织这讨厌的事件冒泡机制呢?

很简单,在事件触发时,会传入一个相应的event对象,其中有一个stopPropagation()方法可以阻止事件冒泡(IE中为cancleBubble=true),以下是详细代码:

    document.getElementById('parent').onclick=function () {
        console.log(this.getAttribute('data-id'));
    };
    document.getElementById('child').onclick=function (ev) {
        var e = ev||window.event;//<span style="color:#FF0000;">IE中event可以通过window.event随时取到,而其他浏览器需要通过参数传递</span>
        console.log(this.getAttribute('data-id'));
        stopPropagation(e);
    };
    function stopPropagation(e) {
        if (e.stopPropagation) {
            e.stopPropagation();
        } else {
            e.cancelBubble = true;
        }
    }

可以通过运行结果来看,事件冒泡成功被阻止了。


阅读到这儿,朋友们有可能会觉得事件冒泡真是烦人,写个事件还要阻止冒泡!不过凡事都有双刃剑,事件冒泡同时给我们带来的还有事件委托这一减少DOM操作的神器。



2.事件委托

    事件委托,首先按字面的意思就能看你出来,是将事件交由别人来执行,再联想到上面讲的事件冒泡,是不是想到了?对,就是将子元素的事件通过冒泡的形式交由父元素来执行。下面经过详细的例子来说明事件委托:


有可能在开发的时候会遇到这种情况:如导航每一个栏目都要加一个事件,你可能会通过遍历来给每个栏目添加事件:

<ul id="parentUl">
        <li>我还是个孩子</li>
        <li>我还是个孩子</li>
        <li>我还是个孩子</li>
        <li>我还是个孩子</li>
        <li>我还是个孩子</li>
    </ul>
 var ul = document.getElementById('parentUl'),
            li = ul.getElementsByTagName('li');
    for (var i = 0; i<li.length;i++){
        li[i].onclick=function () {
            alert(this.innerHTML);
        }
    }


这种方式来添加事件固然简单,但是需要多次操作DOM,如果有100、1000个同级的元素需要添加事件,这种方式简直不忍直视,

而且当我们动态添加新的Li元素的时候,新添加的Li元素是没有被绑定事件的。。。等等!我老王表示不服并给你抛出一段代码:

var ul = document.getElementById('parentUl'),
            li = ul.getElementsByTagName('li');
    function addClick() {
        for (var i = 0; i<li.length;i++){
            li[i].onclick=function () {
                alert(this.innerHTML);
            }
        }
    }
    addClick();
    function addElement() {
        var li = document.createElement('li');
        li.innerHTML="我是新孩子";
        ul.appendChild(li);
        addClick();
    }
    addElement();
这样问题不就解决了么!根本不需要什么事件委托!(#傲娇脸)

这样确实解决了问题但是又增加了操作DOM的次数,大大降低了性能,让我们来看一下通过事件委托是怎样降低DOM操作次数的的:


var ul = document.getElementById('parentUl');
    ul.onclick=function (event) {
      var e = event||window.event,
              source = e.target || e.srcElement;//target表示在事件冒泡中触发事件的源元素,在IE中是srcElement
        if(source.nodeName.toLowerCase() == "li"){   //判断只有li触发的才会输出内容
            alert(source.innerHTML);
        }
        stopPropagation(e);                           //阻止继续冒泡
    };
    function addElement() {
        var li = document.createElement('li');
        li.innerHTML="我是新孩子";
        ul.appendChild(li);
    }


       可以通过运行结果看见,新添加的子元素也可以成功通过事件委托显示内容,红色的为li中包含的span,由于代码中将源元素进行了过滤所以不会输出内容。事件委托不仅实现相同了功能,而且大大减少了DOM操作。有朋友可能会问,那我想要给每个li进行不同的处理呢?当然这个可以解决,我们可以通过ev.target获得源元素,就可以获取到data-id等属性或者自定义属性,并通过判断来进行不同的操作。




   

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页