|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 不二如是 于 2019-1-11 17:32 编辑
很多鱼油关于JS的问题会涉及到事件委托,在0 2 3 2 ★ JS执行机制大作战 中也涉及到了。
还是有必要仔细围绕这个东东,好好说一下。
事件委托的定义:
利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
何为事件冒泡(event bubbling)呢?
这个概念是有微软提出滴,事件冒泡可以形象地比喻为:
也就是说:
事件会从最内层的元素开始发生,一直向上传播,直到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")[0];
- 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[i].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")[0];
- 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[i].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")[0];
- 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() 处理一下。
如果喜欢,别忘了评分 :
这位鱼油,如果喜欢本系列Js帖子,请订阅 专辑☞( 传送门)( 不喜欢更要订阅 )
|
|