事件委托

事件冒泡

事件冒泡:事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播。例如当用户点击了<div>元素,click事件将按照<div>—><body>—><html>—>document的顺序进行传播。若在<div>和<body>上都定义了click事件,则在冒泡过程中会依次触发事件。如下:

<script type="text/javascript">
        var div=document.getElementById("myDiv");
        div.onclick=function(event){
            alert("div");
        };
        document.body.onclick=function(event){
            alert("body");
        };    
</script>

点击<div>,将先输出“div”,再输出“body”。

IE9,chrome,Firefox,Opera,Safari都支持事件冒泡,并将事件冒泡到window对象。


事件捕获

事件捕获是先由最上一级的节点先接收事件,然后向下传播到具体的节点。当用户点击了<div>元素,采用事件捕获,则click事件将按照document—><html>—><body>—><div>的顺序进行传播。

若在<div>和<body>上都定义了click事件,如下:

<script type="text/javascript">
        var div=document.getElementById("myDiv");    
        div.addEventListener("click",function(event){
            alert("div");
        },true);
        document.body.addEventListener("click",function(event){
            alert("body");
        },true);
        
    </script>

点击<div>,将先输出“body”,再输出“div”。

IE9,chrome,Firefox,Opera,Safari都支持事件捕获,但是IE8和IE8以下的版本只支持事件冒泡。尽管DOM2规范要求事件应该从document对象开始传播,但是现在的浏览器实现都是从window对象开始捕获事件。


DOM事件流:

结合上述,兼备捕获和冒泡,先捕获后冒泡。


事件委托

一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

 如下html,如果不用事件委托,将每一个li都去添加click事件监听,非常麻烦。如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能; 另外就是如果通过js动态创建的子节点,需要重新绑定事件。 而利用事件委托的话,只需要给父级绑定一个事件监听,即可让每个li都绑定上相应的事件。
<ul id="line">
    <li></li>
    <li></li>
    <li></li>
    <li></li>
        <!--不定数个li-->
    <li></li>
    <li></li>
</ul>

若要实现点击li标签,对应的li就会消失,只需把事件绑定在ul上,代码如下,

 //找到父元素,添加监听器
document.getElementById("line").addEventListener("click",function (ev) {
        // ev.target是被点击元素
       // 如果被点击的是li元素
        if(ev.target&&ev.target.nodeName=="LI")
        {
            ev.target.remove();
        }
    })        
在上述代码中, target 元素则是在 #line 元素之下具体被点击的元素,然后通过判断 target 的一些属性(比如:nodeName,id 等等)可以更精确地匹配到某一类 #list li 元素之上;
对于复杂的匹配,如上方法则需要多个判定条件,显得很不灵活。此时可以通过使用 Element.matches API来精确匹配。
Element.matches API 的基本使用方法: Element.matches(selectorString),selectorString 是个css选择器字符串.举个例子,ev.target.matches('li.li1')。如果 target 元素是标签 li 并且它的类是 li1 ,那么就会返回 true,否则返回 false;
 

当然它的兼容性还有一些问题,需要 IE9 及以上的现代化浏览器版本;

我们可以使用 Polyfill 来解决兼容性上的问题:

if (!Element.prototype.matches) {
    Element.prototype.matches = 
        Element.prototype.matchesSelector || 
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector || 
        Element.prototype.oMatchesSelector || 
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;            
        };
}

加上 Element.matches 之后就可以来实现我们的需求了:


target和currentTarget的区别

在冒泡模型里,e.currentTarget指的是注册了事件监听器的对象,而e.target指的是该对象里的子对象,例如点击上述例子中li标签,target指向li标签,而currentTarget则指向有监听器的ul标签。

现在,假如li标签里面还包含着<span>之类的标签,而我们点击了<span>标签,此时target指向了<span>,并不是我们所想要的目标元素,故应该加个函数来寻找我们要的元素,代码如下

   while (target !== currentTarget) {  //判断是否到了遍历到了父层
        if (target.matches(targetSelector)) {// 判断是否匹配到我们所需要的元素
            var sTarget = target;
            // 执行绑定的函数,注意 this
           Function~~~
        }

        target = target.parentNode;//若不为所需元素,则将target指向其上一级元素
    }

 


 

jQuery 中的事件委托

jQuery 中的事件委托相信很多人都用过,它主要这几种方法来实现:

    • $.on: 基本用法: $('.parent').on('click', 'a', function () { console.log('click event on tag a'); }),它是 .parent 元素之下的 a 元素的事件代理到 $('.parent') 之上,只要在这个元素上有点击事件,就会自动寻找到 .parent 元素下的 a 元素,然后响应事件;
    • $.delegate: 基本用法: $('.parent').delegate('a', 'click', function () { console.log('click event on tag a'); }),同上,并且还有相对应的 $.delegate 来删除代理的事件;

原文地址:https://www.cnblogs.com/JhonFlame/p/8030509.html