javascript事件代理

來源:互聯網
上載者:User


在編程中,如果我們不想或不能夠直接操縱目標對象,我們可以利用delegate建立一個代理對象來調用目標對象的方法,從而達到操縱目標對象的目的。毋庸置疑,代理對象要擁有目標對象的引用。我們來看一下javascript的一個最簡單實現:

      var delegate = function (client,clientMethod ){        return function() { return clientMethod.apply(client,arguments); }      }      var agentMethod = delegate(client, client.method )      agentMethod();

<br /><!doctype html><br /><html dir="ltr" lang="zh-CN"><br /> <head><br /> <meta charset="utf-8"/><br /> <meta http-equiv="X-UA-Compatible" content="IE=Edge"><br /> <script type="text/javascript"><br /> var ClassA = function(){<br /> var _color = "red";<br /> return {<br /> getColor : function(){<br /> document.write("<p>ClassA的執行個體的私人屬性_color目前是<span style='color:"+_color+"' >"+_color+"</span></p>");<br /> },<br /> setColor:function(color){<br /> _color = color;<br /> }<br /> };<br /> };<br /> var delegate = function (client,clientMethod ){<br /> return function() { return clientMethod.apply(client,arguments); }<br /> }<br /> window.onload = function(){<br /> var a = new ClassA();<br /> a.getColor();<br /> a.setColor("green");<br /> a.getColor();<br /> //alert(a._color);<br /> document.write("<p>執行代理!</p>")<br /> var d = delegate(a,a.setColor);<br /> d("blue");<br /> document.write("<p>執行完畢!</p>")<br /> a.getColor();<br /> };<br /> </script><br /> <title>delegate</title><br /> </head><br /> <body><br /> </body><br /></html><br />

運行代碼

或者我們改變一下第二個參數,傳個字串進去:

      var delegate = function (client,clientMethod ){        return function() { return client[clientMethod].apply(client,arguments); }      }/****************略*******************/     var d = delegate(a,"setColor");

我們還可以搞一些很好玩的東西,下面的例子取自一個日本部落格。

</p><p> <script type="text/javascript"><br /> Function.prototype.delegate = function(delegateFor){<br /> return delegateFor.apply(null, arguments);<br />};</p><p>var Hoge = function(){<br /> this.open = function (){<br /> return 'this is Hoge#open';<br /> };<br /> this.close = function (){<br /> return 'this is Hoge#close';<br /> };<br />};<br />var Foo = function (){<br /> this.name = 'foo';<br /> this.open = function (){<br /> return 'this is Foo#open';<br /> };<br /> this.close = function (){<br /> return 'this is Foo#close';<br /> };<br />};</p><p>var hoge = new Hoge;<br />var foo = new Foo;</p><p>alert(hoge.open()); // this is Hoge#open<br />alert(hoge.open.delegate(foo.open)); // this is Foo#open<br />alert(foo.open.delegate(hoge.open)); // this is Hoge#open<br /> </script><br />

運行代碼

由於delegate實現目標對象的隱藏,這對於我們保護一些核心對象是非常有用的。不過,說實在javascript的delegate基本算是call與apply的傀儡,因為js中只有這2個方法提供了改變當前函數內部this範圍的功能。不過,要實現對類的委託而不是執行個體的委託,這就複雜得多。Prototype.js,YUI與JQuery都有相應的實現。具體自己可去參考它們的源碼。接著下來,我將講述delegate在事件上的應用,這可是個無以倫比的東東,就是改變接聽程式的位置(改變事件的綁定對象)。或者說,它得益於javascript獨特的事件傳播機制,因此實現起來非常容易,大家要好好運行它,我以前也在富文字編輯器中用過。

      $.addEvent(colorPicker,'click',function(){        var e = arguments[0] || window.event,        td = e.srcElement ? e.srcElement : e.target,        nn = td.nodeName.toLowerCase();        if(nn == 'td'){          var cmd = colorPicker.getAttribute("title");          var val = td.bgColor;          _format(cmd,val);          e.cancelBubble = true;          colorPicker.style.display = 'none';        }      });

上面就是我在富文字編輯器擷取前景色彩與背景色的代碼,注意,我是把事件綁定在顏色面板上,而不是面板上的那一個個格子上。為了直觀起見,便於大家操作,我改用下面這個例子:

 <ul id="nav">      <li><a href="http://www.cnblogs.com/">部落格園</a></li>      <li><a href="http://www.blueidea.com/">藍色理想</a></li>      <li><a href="http://www.51js.com/html/bbs.html">無憂指令碼</a></li>      <li><a href="http://www.javaeye.com/">javaeye</a></li>      <li><a href="http://community.csdn.net/">CSDN</a></li></ul>

現在我們要點擊列表中連結,取出裡面的內容,傳統的方法,我們需要遍曆添加所有a元素:

     window.onload = function(){        var nav = document.getElementById("nav");        var links = nav.getElementsByTagName("a");        for (var i=0,l = links.length; i<l; i++) {          links[i].onclick = function () {            alert(this.innerHTML);            return false;           }        }      }

<br /><!doctype html><br /><html dir="ltr" lang="zh-CN"><br /> <head><br /> <meta charset="utf-8"/><br /> <meta http-equiv="X-UA-Compatible" content="IE=Edge"><br /> <style type="text/css"><br /> body{background:#fff;}<br /> a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}<br /> a:hover {color:#336699;border-bottom-color:#B45B3E;}<br /> </style><br /> <script type="text/javascript"><br /> window.onload = function(){<br /> var nav = document.getElementById("nav");<br /> var links = nav.getElementsByTagName("a");<br /> for (var i=0,l = links.length; i <l; i++) {<br /> links[i].onclick = function () {<br /> alert(this.innerHTML);<br /> return false;<br /> }<br /> }<br /> }<br /> </script><br /> <title>delegate</title><br /> </head><br /> <body><br /> <ul id="nav"><br /> <li><a href="http://www.cnblogs.com/">部落格園</a></li><br /> <li><a href="http://www.blueidea.com/">藍色理想</a></li><br /> <li><a href="http://www.51js.com/html/bbs.html">無憂指令碼</a></li><br /> <li><a href="http://www.javaeye.com/">javaeye</a></li><br /> <li><a href="http://community.csdn.net/">CSDN</a></li><br /> </ul><br /> </body><br /></html><br />

運行代碼

新的方法,用nav代理了它下面所有元素,讓它負責大家的onclick事件,包括它自己的,也不管是否為a元素,所以有時我們需要做一些判斷,但少了遍曆DOM樹,效率明顯提高。

      window.onload = function(){        var nav = document.getElementById("nav");        nav.onclick = function () {          var e = arguments[0] || window.event,          target = e.srcElement ? e.srcElement : e.target;          alert(target.innerHTML);          return false;        }      }

<br /><!doctype html><br /><html dir="ltr" lang="zh-CN"><br /> <head><br /> <meta charset="utf-8"/><br /> <meta http-equiv="X-UA-Compatible" content="IE=Edge"><br /> <style type="text/css"><br /> body{background:#fff;}<br /> a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}<br /> a:hover {color:#336699;border-bottom-color:#B45B3E;}<br /> </style><br /> <script type="text/javascript"><br /> window.onload = function(){<br /> var nav = document.getElementById("nav");<br /> nav.onclick = function () {<br /> var e = arguments[0] || window.event,<br /> target = e.srcElement ? e.srcElement : e.target;<br /> alert(target.innerHTML);<br /> return false;<br /> }<br /> }<br /> </script><br /> <title>delegate</title><br /> </head><br /> <body><br /> <ul id="nav"><br /> <li><a href="http://www.cnblogs.com/">部落格園</a></li><br /> <li><a href="http://www.blueidea.com/">藍色理想</a></li><br /> <li><a href="http://www.51js.com/html/bbs.html">無憂指令碼</a></li><br /> <li><a href="http://www.javaeye.com/">javaeye</a></li><br /> <li><a href="http://community.csdn.net/">CSDN</a></li><br /> </ul><br /> </body><br /></html><br />

運行代碼

為什麼它會行得通呢?!因為DOM2.0的事件模型是這樣的,如果某個元素觸發一個事件,如onclick,頂層對象document就會發出一個事件流,隨著DOM樹往目標元素流去,這就是傳說中的捕獲階段,也就是原Netscape的事件執行模式,沿途的元素如果綁定了事件,它是不會執行的!第二階段,就是到達了目標元素,執行它上面的綁定事件,但如果onclick只是個空實現,當然是沒有效果啦!第三階級,就是起泡階級,原IE的事件執行模式,從目標元素往頂層元素折回,如果沿途有onclick事件,就隨個觸發!因此我們是點擊了a元素,但它的onclick事件為空白,當事件流上浮到ul元素時,發現ul元素繫結了onclick事件,就執行當中的函數。如果ul的祖先元素也綁定了onclick事件呢?!繼續執行!有多少執行多少!看下面的例子:

<br /><!doctype html><br /><html dir="ltr" lang="zh-CN"><br /> <head><br /> <meta charset="utf-8"/><br /> <meta http-equiv="X-UA-Compatible" content="IE=Edge"><br /> <style type="text/css"><br /> body{background:#fff;}<br /> a { color:#8080C0;text-decoration:none;border-bottom:2px solid #fff;}<br /> a:hover {color:#336699;border-bottom-color:#B45B3E;}<br /> </style><br /> <script type="text/javascript"><br /> var addEvent = (function () {<br /> if (document.addEventListener) {<br /> return function (el, type, fn) {<br /> el.addEventListener(type, fn, false);<br /> };<br /> } else {<br /> return function (el, type, fn) {<br /> el.attachEvent('on' + type, function () {<br /> return fn.call(el, window.event);<br /> });<br /> }<br /> }<br /> })();</p><p> window.onload = function(){<br /> var nav = document.getElementById("nav");<br /> nav.onclick = function () {<br /> var e = arguments[0] || window.event,<br /> target = e.srcElement ? e.srcElement : e.target;<br /> alert(target.innerHTML);<br /> return false;<br /> }<br /> var wrapper = document.getElementById("wrapper");<br /> addEvent(wrapper,'click',function(){<br /> alert("冒泡過程連我也驚動了!");<br /> });<br /> }<br /> </script><br /> <title>delegate</title><br /> </head><br /> <body><br /> <div id="wrapper"><br /> <ul id="nav"><br /> <li><a href="http://www.cnblogs.com/">部落格園</a></li><br /> <li><a href="http://www.blueidea.com/">藍色理想</a></li><br /> <li><a href="http://www.51js.com/html/bbs.html">無憂指令碼</a></li><br /> <li><a href="http://www.javaeye.com/">javaeye</a></li><br /> <li><a href="http://community.csdn.net/">CSDN</a></li><br /> </ul><br /> </div><br /> </body><br /></html><br />

運行代碼

正由於這個特性,我們就可以利用ul的onclick去代理它下面所有元素的onclick事件。原來,我們需要給這些a元素準備五個接聽程式(EventListener),現在我們只需要1個,節省了4個,如果這個列表有一百行呢?就節省了99個!在商務應用,我們經過會遇到許多報表(grid,實質是用table做的),我們需要為每行添加懸浮變色效果與點擊編輯功能,這就用到onmouseover 、 onmouseout 與 onclick事件,如果這個報表有五千行,我們也只需要三個接聽程式,節省了14997個!如果用傳統方法做這個grid,IE6這樣垃圾的遊覽器不卡到你吐血。因此,善用event delegation會大大提高我們程式的效能。好了,文章到此為止,再見。

附上一些有用的連結:

  • Event delegation with jQuery
  • Event delegation with Prototype
  • Event delegation with YUI

http://blog.xole.net/article.php?id=632

http://htmlblog.net/bubble-menu-javascript-or-playing-with-yuis-event-delegation/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.