javascript事件系統的發展史

來源:互聯網
上載者:User

一個完整的事件系統,通常存在以下三個角色:

  • 事件對象,用於儲存事件的狀態。
  • 事件來源對象,當前事件在操作的對象,如元素節點,文檔對象,window對象,XMLHttpRequest對象等。
  • 事件監聽器,當一個事件來源產生一個事件對象時,它會調用相應的回呼函數進行操作。在IE中,事件對象恒為全域屬性window.event的分身。

在w3c沒有把其DOM 模型引入網頁時,netscape與微軟已經逼不及待到快他們熟悉的語言中把相關的DOM模型搞進來了。這其實也怪javascript之父忙於把抄襲其他語言,忽略了自身事件系統的建設。從此世界被劃分為兩大陣營了。

DOM0時代,這裡的DOM指w3c的DOM。雙方都設計兩種綁定事件的方法,無侵入式與侵入式。你可以說內聯式與非內聯式的區別。

侵入式,雙方都一樣。沒有辦法,那是很早就實現的。那時IE只有抄襲的份,還不敢胡來。

<input name="ruby" onclick="alert(this.nam)" />

然後是無侵入式,這估計是它們都完成了各自的DOM模型,實現對元素節點的索引機制之後的事了。比如有以下網頁片斷:

    

我們必須自上而下,一步步找到此元素節點才能操作它。注意,那時沒有所謂的document.getElementById。網景的做法,把相關綁定的代碼放進一個script標籤中:

    <form name="form1">      <input        type="button"        name="button1"        value="aaaa"/>    </form>

如果你不想把代碼用window.onload = function(){}這代碼塊括起來,那麼你得把這script標籤放於表單元素之後。

微軟也有一套索引機制,基本與網景的一樣,但IE4還引入了document.all與document.all.tags。不過IE還有另一套方式:

   <script      for="button1"      event="onclick"      language="JavaScript">        alert("this.aaa")    </script>

不過,它用不了this(或者能,我不會),另要求一個script標籤對應body中的一個標籤,實在很浪費,最終被淘汰出局了。

這就是DOM0的綁定機制,另以內聯方式寫在標籤中的代碼,其實相當於以下方式:

    <p id="aa" onclick="alert('aaaa')">相當於↓</p>    <script type="text/javascript">      var p = document.getElementById("aa")      p.onclick = new Function("alert('aaaa')")//相當於↓      p.onclick = function(){alert('aaaa')}    </script>

至此,事件系統三個角色都出場了。通過索引機製得到的對象(元素節點什麼的),作為事件來源,onclick,onmousemove之類的事件屬性,它們充當監聽器,onclick後面的函數就是回呼函數,這是非同步執行的。

隨著無侵入的興起,放到web標準中,應該叫做表現行為結構相分離。在標籤內寫onclick什麼的應該唾棄。無侵入式編程有一種讓人越寫越多代碼的慾望。以前總是縮在一個標籤內,隨時注意雙引號與單引號的套嵌,寫多了就煩了,不想寫了,現在沒有這限制,就像脫韁的馬,把更多注意力用於相容更多瀏覽器與創造新的點子上。好了,寫著寫著,人們就開始想能不能在同一個元素上綁定兩個onclick事件呢?!

    <script type="text/javascript">      var p = document.getElementById("aa")      p.onclick = function(){alert('第一次')}      p.onclick = function(){alert('第二次')}    </script>

當然,只能alert第二個,我們當然也可以用一些技巧達到這目的:

    <p id="aa" onclick="alert('第一次')">能綁定多個同類型函數</p>    <script type="text/javascript">      var p = document.getElementById("aa")      var addEvent = function(el,type,fn) {        var type = "on"+type        var old = el[type];        if (typeof el[type] != 'function') {          el[type] = fn        }else {          el[type] = function() {            old();            fn();          }        }      }      addEvent(p,"click",function(){alert('第二次')});      addEvent(p,"click",function(){alert('第三次')});   </script>

<br /><!doctype html><br /><html lang="zh-ch"><br /> <head><br /> <meta charset="utf-8" /><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <title>綁定多個事件對象 by 司徒正美</title><br /> <script type="text/javascript"><br /> window.onload = function(){<br /> var p = document.getElementById("aa")<br /> var addEvent = function(el,type,fn) {<br /> var type = "on"+type<br /> var old = el[type];<br /> if (typeof el[type] != 'function') {<br /> el[type] = fn<br /> }else {<br /> el[type] = function() {<br /> old();<br /> fn();<br /> }<br /> }<br /> }<br /> addEvent(p,"click",function(){alert('第二次')});<br /> addEvent(p,"click",function(){alert('第三次')});<br /> }<br /> </script></p><p> </head><br /> <body></p><p> <p id="aa" onclick="alert('第一次')">能綁定多個同類型函數(請點我)</p><br /> </body><br /></html><br />

運行代碼

但當要使用者搞這東西是不行,因此瀏覽器商把它們做成內建的。順帶還搞了個事件流,也就是允許事件對象在控制項間(標籤)中傳遞。IE的一套API是createEventObject, attachEvent, dettachEvent, fireEvent,事件流是自下向上。網景那套就不清楚了,但聽說w3c也是從它那一套發展而來,API比較複雜,createEvent, initEvent,addEventListener, removeEventListener dispatchEvent,那個initEvent還有許多版本呢,如initMouseEvent, initKeyEvent,參數非常多,用於更精確的配置。addEventListener擁有三個參數,但第三個參數通常只在事件代理中有用,通常為false,與IE保持一致,自下而上的冒泡。由於w3c的劣性根,總想與IE劃分界線,它最高能冒泡到window(IE為document):

        event = dom.Event(type);        args =  [event].concat(args);        var parent = caller;        while(!event.isPropagationStopped() && parent){//isPropagationStopped為w3c dom的一個方法,          dom.events.handle.apply( parent,args);     //判定是否已禁止冒泡          parent = parent.parentNode || (parent != window) && window;        }

很奇怪的是HTML的parentNode竟然是文檔對象。如果是捕獲就麻煩多了,這裡不談它。現在看一下多事件綁定時的相容問題吧。比如上面那個addEvent其實夠用了,DE大神的addEvent也是根據DOM0事件搞出來的。但有一些事件是DOM0絕對類比不了,如FF的DOMMouseScroll事件,因為沒有onDOMMouseScroll這個屬性,它必須要用addEventListener,但IE,opera,chrome等支援的mousewheel。因此我們還是離不開這些進階的API。一個通用addEvent函數:

var addEvent = (function () {    if (document.addEventListener) {        return function (el, type, fn) {            el.addEventListener(type, fn, false);        };    } else {        return function (el, type, fn) {            el.attachEvent('on' + type, function () {                return fn.call(el, window.event);            });        }    }})();

不過還是有問題,IE下綁定回呼函數不是先進先出,詳見《IE與非IE瀏覽器在事件綁定的執行順序問題》。嗯,這些我將留在下一部分講。

PS,這個系列與《javascript 跨瀏覽器的事件系統》系列是不一樣,這裡著重講述設計一個事件系統遇到的各種各樣的問題。而後者則給出具體的解決方案。

相關文章

聯繫我們

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