javascript 跨瀏覽器的事件系統3

來源:互聯網
上載者:User

這部分說一下最近非常流行的事件代理。事件代理的實現簡單來說,是把事件綁定到目標元素的祖先元素上,然後通過冒泡或捕獲得到事件來源,然後再判定事件來源是否等於目標元素再執行回呼函數。由於對目標元素的判定有時非常模糊,因此通過判定即可調用回呼函數,這樣,我們就達到一個監聽器為許多事件來源服務的目的。對於效能一向非常不怎麼樣的IE6來說,實在幫了一個大忙。

假如,有一個無序列表,點擊彈出它的innerHTML,如果按事件綁定的做法,代碼大概是這樣:

      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 els = document.getElementsByTagName("li")        for(var i=0,n=els.length;i<n;i++){          addEvent(els[i],"click",function(){            alert(this.innerHTML)          })        }      }

<br /><!doctype html><br /><html lang="zh-ch" id="html"><br /> <head><br /> <meta charset="utf-8" /><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <title>事件代理 by 司徒正美</title></p><p> <script type="text/javascript"></p><p> window.onload = function(){<br /> var addEvent = function(el,type,callback,data){<br /> if ( el.addEventListener ) {//如自訂對象就綁定回呼函數了<br /> el.addEventListener( type, callback, !!data );<br /> } else if ( el.attachEvent ) {<br /> el.attachEvent( "on" + type, function(){<br /> return callback.call(el,window.event)<br /> } );<br /> }<br /> }<br /> var els = document.getElementsByTagName("li")<br /> for(var i=0,n=els.length;i<n;i++){<br /> addEvent(els[i],"click",function(){<br /> alert(this.innerHTML)<br /> })<br /> }<br /> }</p><p> </script></p><p> </head><br /> <body id="body"><br /> <h1>事件代理</h1><br /> <ul><br /> <li>無序列表1</li><br /> <li>無序列表2</li><br /> <li>無序列表3</li><br /> <li>無序列表4</li><br /> </ul><br /> </body><br /></html><br />

運行代碼

如果用事件代理的做法大概是這樣:

         var ul = document.getElementsByTagName("ul")[0]        addEvent(ul,"click",function(e){          var el = e.srcElement || e.target;          if(el.tagName === "LI")            alert(el.innerHTML)        })

<br /><!doctype html><br /><html lang="zh-ch" id="html"><br /> <head><br /> <meta charset="utf-8" /><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <title>事件代理 by 司徒正美</title></p><p> <script type="text/javascript"></p><p> window.onload = function(){<br /> var addEvent = function(el,type,callback,data){<br /> if ( el.addEventListener ) {//如自訂對象就綁定回呼函數了<br /> el.addEventListener( type, callback, !!data );<br /> } else if ( el.attachEvent ) {<br /> el.attachEvent( "on" + type, function(){<br /> return callback.call(el,window.event)<br /> } );<br /> }<br /> }<br /> var ul = document.getElementsByTagName("ul")[0]<br /> addEvent(ul,"click",function(e){<br /> var el = e.srcElement || e.target;<br /> if(el.tagName === "LI")<br /> alert(el.innerHTML)<br /> })<br /> }</p><p> </script></p><p> </head><br /> <body id="body"><br /> <h1>事件代理</h1><br /> <ul><br /> <li>無序列表1</li><br /> <li>無序列表2</li><br /> <li>無序列表3</li><br /> <li>無序列表4</li><br /> </ul><br /> </body><br /></html><br />

運行代碼

但是這樣複用性不好,我們需要一個真正的事件代理函數。如果用YUI與jQuery的人應該知道,這其中必然涉及到選取器,利用選取器進行匹配,實現上面的el.tagName === "LI",只不過適用性更廣罷了。但現在我們不可能搞鼓個選取器出來,那可不是一下子就能實現的東西,況且它與我們的主題,為簡單起見,我們就使用原生的標籤選取器。換言之,新函數的參數之一,就是目標元素的tagName。其次,我們需要一個或一些代理元素,就像上面例子那個ul元素。我在《事件冒泡》一文中也給出各元素冒泡的情況了,若無視不能冒泡的事件,那麼元素大抵是分上浮到from元素與上浮到文檔這兩種情況,當然還有上浮到window的情況,但最高只能上浮document。現在我們無視這些情況,強製為document。

       var delegate = function(a,type,callback){          var els = document.getElementsByTagName(a);          addEvent(document,type,function(e){            var el = e.srcElement || e.target;            for(var i=0,n=els.length;i<n;i++){              if(el === els[i]){                return callback.call(el,e)              }            }          },true);        }        delegate("li","click",function(e){          alert(this.innerHTML+e.type)        });

<br /><!doctype html><br /><html lang="zh-ch" id="html"><br /> <head><br /> <meta charset="utf-8" /><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <title>事件代理 by 司徒正美</title></p><p> <script type="text/javascript"></p><p> window.onload = function(){<br /> var addEvent = function(el,type,callback,data){<br /> if ( el.addEventListener ) {//如自訂對象就綁定回呼函數了<br /> el.addEventListener( type, callback, !!data );<br /> } else if ( el.attachEvent ) {<br /> el.attachEvent( "on" + type, function(){<br /> return callback.call(el,window.event)<br /> } );<br /> }<br /> }<br /> var delegate = function(a,type,callback){<br /> var els = document.getElementsByTagName(a);<br /> addEvent(document,type,function(e){<br /> var el = e.srcElement || e.target;<br /> for(var i=0,n=els.length;i<n;i++){<br /> if(el === els[i]){<br /> return callback.call(el,e)<br /> }<br /> }<br /> },true);<br /> }<br /> delegate("li","click",function(e){<br /> alert(this.innerHTML+e.type)<br /> });</p><p> }<br /> var aaa = function(){<br /> var li = document.createElement("li");<br /> li.innerHTML = "動態添加"+(aaa.aa++)<br /> document.getElementsByTagName("ul")[0].appendChild(li)<br /> }<br /> aaa.aa = 0<br /> </script></p><p> </head><br /> <body id="body"><br /> <ul><br /> <li>無序列表1</li><br /> <li>無序列表2</li><br /> <li>無序列表3</li><br /> <li>無序列表4</li><br /> </ul><br /> <button type="button" onclick="aaa()">動態添加</button><br /> </body><br /></html><br />

運行代碼

這裡有個奇妙的現象,當我們執行delegate函數,LI元素的個數為4,每當我們動態添加一個時,它也會相應添加一個。輕鬆實現jQuery所謂的live函數。不過,當我們把els轉換為純數組時,它就失去這種動態性了。jQuery的選取器等價於getElementsByTagName嗎?不等於!因此,我們要重新來過了!

        var delegate = function(a,type,callback){          var els = document.getElementsByTagName(a),          tmp = [];          for(var j=0,jn=els.length;j<jn;j++){            tmp.push(els[j])          }          els = tmp;//強制轉換為純數組          addEvent(document,type,function(e){            var el = e.srcElement || e.target;            for(var i=0,n=els.length;i<n;i++){              if(el === els[i]){                return callback.call(el,e)              }            }          },true);        }

<br /><!doctype html><br /><html lang="zh-ch" id="html"><br /> <head><br /> <meta charset="utf-8" /><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <title>事件代理 by 司徒正美</title></p><p> <script type="text/javascript"></p><p> window.onload = function(){<br /> var addEvent = function(el,type,callback,data){<br /> if ( el.addEventListener ) {//如自訂對象就綁定回呼函數了<br /> el.addEventListener( type, callback, !!data );<br /> } else if ( el.attachEvent ) {<br /> el.attachEvent( "on" + type, function(){<br /> return callback.call(el,window.event)<br /> } );<br /> }<br /> }<br /> var delegate = function(a,type,callback){<br /> var els = document.getElementsByTagName(a),<br /> tmp = [];<br /> for(var j=0,jn=els.length;j<jn;j++){<br /> tmp.push(els[j])<br /> }<br /> els = tmp;//強制轉換為純數組<br /> addEvent(document,type,function(e){<br /> var el = e.srcElement || e.target;<br /> for(var i=0,n=els.length;i<n;i++){<br /> if(el === els[i]){<br /> return callback.call(el,e)<br /> }<br /> }<br /> },true);<br /> }<br /> delegate("li","click",function(e){<br /> alert(this.innerHTML+e.type)<br /> });</p><p> }<br /> var aaa = function(){<br /> var li = document.createElement("li");<br /> li.innerHTML = "動態添加"+(aaa.aa++)<br /> document.getElementsByTagName("ul")[0].appendChild(li)<br /> }<br /> aaa.aa = 0<br /> </script></p><p> </head><br /> <body id="body"><br /> <ul><br /> <li>無序列表1</li><br /> <li>無序列表2</li><br /> <li>無序列表3</li><br /> <li>無序列表4</li><br /> </ul><br /> <button type="button" onclick="aaa()">動態添加</button><br /> </body><br /></html><br />

運行代碼

為了盡量接近現實中的複雜需求,我最後還是給出一個小型選取器吧,注意只能運行於IE8(要求是標準模式)與較新的標準瀏覽器下。為了實現jQuery.live的效果,我們對代碼做了一些調整:

      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 delegate = function(a,type,callback){          addEvent(document,type,function(e){            var els = $(a);            var node = e.srcElement || e.target;            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)        });

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

運行代碼

就這樣,一個粗糙的事件代理系統完成了。但有個問題,我們每次進入這個函數,就調用選取器,是不是很耗效能呢?因為動態添加的情況並不是每次都發生,我們只要比較原來的節點集合就行了,當發現原來的找不到,再調用選取器也不晚。第四部分我們將解決這問題,see you!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.