javascript 跨浏览器的事件系统4

本文介绍了一种优化事件代理的方法,通过缓存节点集合减少重复查询,提高性能,并引入包含判定处理深层元素触发的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上次说到我们在无菌环境中设计了一个事件代理函数,但性能是个问题,解决它我们需要缓存节点集合,发现节点集合不足以应对新情况时,再替换这个节点集合,重新开始匹配。下面是新的方案:

 
        var delegate = function(selector,type,callback){
          var els = $(selector);
          addEvent(document,type,function(e){
            var flag = true;
            var node = e.srcElement || e.target;
            for(var i=0,el;el = els[i++];){
              if(node === el){
                flag = false;
                return callback.call(node,e);
              }
            }
            if(flag){
              els = $(selector);
              for(var i=0,el;el = els[i++];){
                if(node === el){
                  return callback.call(node,e);
                }
              }
            }
          },true);
        }
<!doctype html> <html lang="zh-ch" id="html"> <head> <meta charset="utf-8" /> <meta content="IE=8" http-equiv="X-UA-Compatible"/> <title>事件代理 by 司徒正美</title> <script type="text/javascript"> window.onload = function(){ var addEvent = function(el,type,callback,data){ if ( el.addEventListener ) {//如自定义对象就绑定回调函数了 el.addEventListener( type, callback, !!data ); } else if ( el.attachEvent ) { el.attachEvent( "on" + type, function(){ return callback.call(el,window.event) }); } } var delegate = function(selector,type,callback){ var els = $(selector); addEvent(document,type,function(e){ var flag = true; var node = e.srcElement || e.target; for(var i=0,el;el = els[i++];){ if(node === el){ flag = false; return callback.call(node,e); } } if(flag){ els = $(selector); for(var i=0,el;el = els[i++];){ if(node === el){ return callback.call(node,e); } } } },true); } delegate("#list li","click",function(e){ alert(this.innerHTML+e.type) }); } var $ = function(selector,context){ context = context || document try{ var els = context.querySelectorAll(selector), result = [],ri=0,i=0,n=els.length; for(;i<n;i++){ result[ri++] = els[i] } return result; }catch(e){ alert("你的浏览器不支持querySelectorAll") } } var aaa = function(){ var li = document.createElement("li"); li.innerHTML = "动态添加"+aaa.aa++ $("#list")[0].appendChild(li) } aaa.aa = 0; </script> </head> <body id="body"> <h1>司徒正美:事件代理</h1> <ul id="list"> <li>无序列表1</li> <li>无序列表2</li> <li>无序列表3</li> <li>无序列表4</li> </ul> <p>下面的无序列表是没有id的!</p> <ul> <li>无序列表1</li> <li>无序列表2</li> <li>无序列表3</li> <li>无序列表4</li> </ul> <button type="button" onclick="aaa()">动态添加</button> </body> </html>

运行代码

好了,我们现在来考虑另一种情况。之前我们的判定都是用全等于来比较,但如果事件源是来自更底层的元素呢?换言之,是下面的情况。

 
    <div onclick="alert('outer')" id="outer">
      <div onclick="alert('middle')" id="middle">
        <div onclick="alert('inner')" id="inner"></div>
      </div>
    </div>

当我们点击inner元素时,它上面的middle与outer的onclick也触发,因此我们必须引入包含判定了。这里我直接给出答案,具体可见我的这一篇博文

 
      var  contains = function(el, root) {
        if (el.compareDocumentPosition)
          return (el.compareDocumentPosition(root) & 8) === 8;
        if (root.contains && el.nodeType === 1){
          return root.contains(el) && root !== el;
        }
        while ((el = el.parentNode))
          if (el === root) return true;
        return false;
      }
      var delegate = function(selector,type,callback){
        var els = $(selector);
        addEvent(document,type,function(e){
          var flag = true;
          var src = e.srcElement || e.target;
          for(var i=0,el;el = els[i++];){
            if(el === src || contains(src,el) ){
              flag = false;
              return callback.call(el,e);
            }
          }
          if(flag){
            els = $(selector);
            for(var i=0,el;el = els[i++];){
              if(el === src || contains(src,el) ){
                return callback.call(el,e);
              }
            }
          }
        },true);
      }
<!doctype html> <html lang="zh-ch" id="html"> <head> <meta charset="utf-8" /> <meta content="IE=8" http-equiv="X-UA-Compatible"/> <title>事件代理 by 司徒正美</title> <script type="text/javascript"> window.onload = function(){ var addEvent = function(el,type,callback,data){ if ( el.addEventListener ) {//如自定义对象就绑定回调函数了 el.addEventListener( type, callback, !!data ); } else if ( el.attachEvent ) { el.attachEvent( "on" + type, function(){ return callback.call(el,window.event) }); } } var delegate = function(selector,type,callback){ var els = $(selector); addEvent(document,type,function(e){ var flag = true; var src = e.srcElement || e.target; for(var i=0,el;el = els[i++];){ if(el === src || contains(src,el) ){ flag = false; return callback.call(el,e); } } if(flag){ els = $(selector); for(var i=0,el;el = els[i++];){ if(el === src || contains(src,el) ){ return callback.call(el,e); } } } },true); } delegate("#list li","click",function(e){ alert(this.innerHTML+e.type) }); } var $ = function(selector,context){ context = context || document try{ var els = context.querySelectorAll(selector), result = [],ri=0,i=0,n=els.length; for(;i<n;i++){ result[ri++] = els[i] } return result; }catch(e){ alert("你的浏览器不支持querySelectorAll") } } var contains = function(el, root) { if (el.compareDocumentPosition) return (el.compareDocumentPosition(root) & 8) === 8; if (root.contains && el.nodeType === 1){ return root.contains(el) && root !== el; } while ((el = el.parentNode)) if (el === root) return true; return false; } var aaa = function(){ var li = document.createElement("li"); li.innerHTML = "动态添加"+aaa.aa++ $("#list")[0].appendChild(li) } aaa.aa = 0; </script> <style type="text/css"> #inner{ padding:10px; width:50px; height:50px; background:#4DC2F2; } #middle{ padding:10px; width:100px; height:100px; background:#FF98ED; } #outer{ padding:10px; width:150px; height:150px; background:#00FF00 } </style> </head> <body id="body"> <h1>司徒正美:事件代理</h1> <ul id="list"> <li>无序列表1</li> <li>无序列表2</li> <li>无序列表3</li> <li>无序列表4</li> </ul> <p>下面的无序列表是没有id的!</p> <ul> <li>无序列表1</li> <li>无序列表2</li> <li>无序列表3</li> <li>无序列表4</li> </ul> <button type="button" onclick="aaa()">动态添加</button> <div onclick="alert('outer')" id="outer"> <div onclick="alert('middle')" id="middle"> <div onclick="alert('inner')" id="inner"></div> </div> </div> </body> </html>

运行代码

我们再把筛选事件源的逻辑独立出来,就变成下面这个样子。是时候考虑如第一部分设计的事件系统衔接起来了!

 
        var handle = function(e,obj){
          var flag = true, 
          src = e.srcElement || e.target,
          els = obj.nodes;
          for(var i=0,el;el = els[i++];){
            if(el === src || contains(src,el) ){
              flag = false;
              return obj.callback.call(el,e);
            }
          }
          if(flag){
            els = obj.nodes = $(obj.selector);
            for(var i=0,el;el = els[i++];){
              if(el === src || contains(src,el) ){
                return obj.callback.call(el,e);
              }
            }
          }
        }
        var delegate = function(selector,type,callback){
          var handleObj = {};
          handleObj.callback = callback;
          handleObj.selector = selector;
          handleObj.nodes = $(selector);
          addEvent(document,type,function(e){
            handle(e,handleObj)
          },true);
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值