不二如是 发表于 2019-1-4 16:23:10

0 2 3 6 ★ 原生JavaScript的事件委托

本帖最后由 不二如是 于 2019-1-11 17:32 编辑

上一篇:0 2 3 5 ★ Web课程input上传并显示 |【番外篇】

static/image/hrline/2.gif

很多鱼油关于JS的问题会涉及到事件委托,在0 2 3 2 ★ JS执行机制大作战 中也涉及到了。

还是有必要仔细围绕这个东东,好好说一下。

事件委托的定义:
利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

何为事件冒泡(event bubbling)呢?

这个概念是有微软提出滴,事件冒泡可以形象地比喻为:
**** Hidden Message *****



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

因此在事件冒泡的概念下在 p 元素上发生 click 事件的顺序应该是:
p -> div -> body -> html -> document

大概了解到这一层面就够了,有机会关于“事件捕获和冒泡”,好好讲一下~

继续回到事件委托,在 js 中添加到页面上的事件处理程序的个数直接影响到网页的运行性能。

因为每个事件处理函数都是一个对象,是对象就会占用内存,而内存中对象越多,导致的结果就是性能越差。

而访问 dom 的次数越多,就会引起结构的重绘或者重排的次数也随之增多,会延迟整个页面的交互就绪时间。

这样对于在页面进行处理过后新增的 dom 元素,运用事件委托可以为新增的dom元素一并增减事件处理程序。

理论就是上面这样啦,进入敲代码环节。

我们实现一个p元素单击时,背景变绿的交互,创建基本代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>事件委托</title>
</head>
<body>
<div id="nodes">
    <P class="node_p">第一个p</P>
    <P class="node_p">第二个p</P>
    <P class="node_p">第三个p</P>
    <div id="childDiv">
      <p>这是子集菜单</p>
      <div>我是子集的div
            <p>我是子集的p</p>
      </div>
    </div>
</div>
<button>单击新增一个p元素</button>
</body>
</html>

接下来实现 JS 代码部分,我们先不要用事件委托,实现:
获取页面中所有的 p 元素然后用 for 循环遍历给每一个元素增加事件处理函数

创建JS:
<script>
    var nodes = document.getElementById("nodes");
    var ps = document.getElementsByTagName("p");
    console.log(ps);
    var btn = document.getElementsByTagName("button");
    var inner = 33;
    btn.onclick = function() {
      inner++;
      var p = document.createElement("p");
      p.innerHTML = inner + "新增的p标签啊";
      nodes.appendChild(p);
      console.log(ps);
    };
    for (var i= 0;i<ps.length;i++){
      ps.onclick= function(){
            this.style.background = 'green'
      }
    }
</script>


然后单击p元素会看到:


原来存在的 p 元素单击后背景色正确变绿了,而新增加的p元素没有成功!

怎么肥四?

可以理解为浏览器的一种优化,如果每一个未经授权的新增元素获得所有现存的方法岂不乱套?

需要为新增的元素增加一个事件处理函数。

新增一个 myAddEvent() :
var nodes = document.getElementById("nodes");
    var ps = document.getElementsByTagName("p");
    console.log(ps);
    var btn = document.getElementsByTagName("button");
    var inner = 33;
    btn.onclick = function() {
      inner++;
      var p = document.createElement("p");
      p.innerHTML = inner + "新增的p标签啊";
      nodes.appendChild(p);
      // 将新dom元素增加到页面后在执行
      myAddEvent();
      console.log(ps);
    };

    // 增加事件处理函数,包裹for循环
    function myAddEvent(){
      for (var i= 0;i<ps.length;i++){
      ps.onclick= function(){
            this.style.background = 'green'
      }
    }
    }


搞定啦!

这时候虽然解决了为新增dom元素增加事件处理函数的问题,但是仔细考虑挥会发现:
性能却是比之前都有所下降。

究其原因就是又增加了一个事件处理函数(对象),又一次占用了内存。

所以,这个时候就会用到事件委托,这时候事件委托的优势也有所体现出来:
var nodes = document.getElementById("nodes");
    var ps = document.getElementsByTagName("p");
    console.log(ps);
    var btn = document.getElementsByTagName("button");
    var inner = 33;
    btn.onclick = function() {
      inner++;
      var p = document.createElement("p");
      p.innerHTML = inner + "新增的p标签啊";
      nodes.appendChild(p);
      console.log(ps);
    };

    // 事件委托
    nodes.onclick = function(e){
      var ev = e || window.event;
      var target = ev.target;
      //这里要判被处理元素节点的名字
      if(target.nodeName.toLowerCase() == 'p'){
            target.style.background = 'green'
      }
    }


通过 if 我们判被处理元素节点的名字。

也可以增加相应的判断条件:
target.nodeName.toLowerCase() == 'p'||target.nodeName.toLowerCase() == 'span'

但是要注意不要使用父级元素的名称,因为再点击子元素之间的空气的时候,由于事件冒泡他会给父级元素也增加相应的事件处理函数。

因为返回的节点名称一般都是大写,所以这时要用 toLowerCase() 处理一下。

static/image/hrline/2.gif


下一篇:0 2 3 7 ★ 矮马真诡异的#number



如果喜欢,别忘了评分{:10_281:} :

http://xxx.fishc.com/forum/201709/19/094516hku92k2g4kefz8ms.gif

这位鱼油,如果喜欢本系列Js帖子,请订阅 专辑☞(传送门)(不喜欢更要订阅{:10_297:} )

TCY 发表于 2019-1-11 11:11:39

终于更新了

。Pro 发表于 2019-3-10 20:12:10

谢谢楼主

Jun838480981 发表于 2019-11-5 23:07:15

{:5_103:}

tianyuan 发表于 2020-5-11 14:33:24

{:10_277:}

kkiimm996 发表于 2020-11-21 14:48:57

kk

Rydia 发表于 2021-6-13 11:06:18

。。。

python_zbl 发表于 2021-9-26 08:57:01

{:10_249:}

碉堡的小小 发表于 2022-1-21 14:57:18

1

jack6666 发表于 2022-10-14 14:23:05

1
页: [1]
查看完整版本: 0 2 3 6 ★ 原生JavaScript的事件委托