文章出處

  被我拖延了將近一個月的javascript事件模型系列終于迎來了第四篇,也是我計劃中的最后一篇,說來太慚愧了,本來計劃一到兩個星期寫完的,誰知中間遇到了很多事情,公司的個人的,搞的自己心煩意亂浮躁了一段時間,好在最近這些事情都一件件趨于平息,我也有了精力繼續寫文章。

  這個自定義事件其實是挺讓我糾結的,首先自己平時從未使用過,只是有一次遇到一個問題有人指點說可以用自定義事件,才對這個東西有了印象。在網上搜“javascript自定義事件”,發現也有不少文章在寫,不過說實話讓我佩服的卻一篇也沒找到,就連張鑫旭大哥寫的漫談javascript自定義事件也把我看的云里霧里。陸續查閱了一些資料后越發覺得自定義事件這個東西真是個雞肋,沒什么用武之地。這也使我一度想放棄寫這篇文章,但后來自己又進行了一些思考,并有了一些新的想法,所以在此還是寫出來與大家分享。本來想大手一揮書寫標題“javascript自定義事件”,轉而一想這個標題真是被用濫了,滿大街走的都是長一個樣的,誰能認得我呀,親,我跟他們可真的不太一樣哦。所以前面加上了“我所理解的”,并不全是為標題突出,其實真的有很多自己的理解。

一、什么是自定義事件

  這個其實并不難理解,js中有很多的事件,如click(單擊)、dbclick(雙擊)、mouseover(鼠標移上)等等,大部分是一些鼠標或鍵盤事件,當然也還有其他的如文檔的load,只不過我們平時更加關注前者,因為畢竟是要和用戶打交道嘛,這些可以統稱為DOM事件(都是發生在DOM元素上)。何為自定義DOM事件呢?舉個栗子:元素有單擊、雙擊事件,我現在想定義一個三擊事件,即元素被連續點擊了三次,就給它起名字叫tripleclick。這就是一個自定義的事件。再來個栗子:元素的內部html發生變化,我想監聽到此事件,于是可以定義一個htmlchange事件。概念就是這么回事。

  既然有DOM事件,那有沒有非DOM事件呢?動腦筋想一想。有了~前段時間剛好研究過history API,其中點擊了瀏覽器后退按鈕會觸發popstate事件,這就是個非DOM事件。那我們可以自定義些什么非DOM事件呢?再開闊一下思維,javascript好歹也是一門編程語言呀,除了操作DOM、BOM之外還有很多事情可以做呢。比如我寫了一個dog對象,也可以給它自定義個事件bark(狗叫),一旦發生了這個事件,我們可以捕捉到,在onbark處理函數中執行一些操作,如把dog給趕走。

  那么,所謂自定義事件就是起個名字?這就完了?先埋個伏筆,后面來談談我的看法。

二、如何自定義事件

  關于實現自定義事件的方式,我搜索中文的網頁大概也就兩種方式,而且就是那么幾篇文章被抄來抄去,實在是乏味。總結一下:

  第一種方式是自己模擬一個事件結構,其原理是這樣的,我們平時監聽事件的時候其實就是一種觀察者模式,舉個例子吧更明確些。

<input type="button" value="點我" onclick="clickhandler()" />
<script type="text/javascript">
function clickhandler(){
    alert('點你怎么了!');
}
</script>

  在這里被觀察的主體就是這個button,有一個handler訂閱了它的點擊事件,當被點擊時,button會發布自己被點擊的消息,handler接收到消息便開始執行處理函數。是相當標準的一個觀察者模式。

  照著這個思路,我們可以把整個過程用代碼模擬出來,而不使用瀏覽器的事件機制,讓這個button發布一點其他的消息,比如我們霸氣的“三擊”,然后寫一個handler來監聽這個三擊事件即可。具體的實現例子我就不寫了,因為我覺得這個模擬的辦法簡直是太土了,根本拿不上臺面,想研究的可以看下這篇文章http://www.jb51.net/article/33697.htm 盡管我很惡心腳本之家這種隨便剽竊別人文章的行為,但抱歉我真的找不到出處了。。。

  看過了第一種土的掉渣的方式,我們再來看看高端洋氣的寫法。說白了,其實w3c已經定義了標準的自定義事件寫法了。

  第二種方式如下:

var e = document.createEvent('Event');//創建一個Event對象e
e.initEvent('myevent',true,true);//進行事件初始化
var d1 = document.getElementById('d1');//獲取DOM元素
d1.addEventListener('myevent',function(event){
    alert(‘我監聽到了自定義事件’+event.type);
},false);//綁定監聽器
d1.dispatchEvent(e);//觸發該事件

  使用標準方法還是相當簡單的,首先利用document的createEvent方法可以創建一個事件對象,createEvent接收一個參數表示事件的構造器,如Event、MouseEvent、UIEvent、CustomEvent,至于這些事件類都有哪些這里就不詳細講了,你可以查看我之前寫的系列,有提到相關內容可以追蹤鏈接。然后使用initEvent函數進行事件的初始化,接收的參數分別表示事件的類型、是否冒泡、是否可以用preventDefault()函數禁止默認行為,在這里你就可以為自定義事件起名字了。然后我們注冊監聽器并觸發事件,這樣d1便能監聽到自己定義的事件了,ok,就這么簡單!

  本來自定義事件的方式就該到此結束了,一個小小的意外,我搜到了一篇國外的文章,看到了如下字樣:

  來自mozilla開發者官網,說的就是上面的第二種方式。deprecated?啥意思?google之,藐視的意思!這種方式已經被藐視了哇!竟然還在國內的各網站中被轉來轉去,國外的同仁正在藐視我們。。。不能忍!趕快看看現在都用什么方式了。

  第三種:

var event = new CustomEvent('build', { 'detail': elem.dataset.time });//區別就在這里~
elem.addEventListener('build', function (e) { ... }, false);
elem.dispatchEvent(event);

  原來是直接創建Event對象,取代了原來的document.createEvent(),而且事件的初始化工作也在這里完成了,不必調用initEvent()了。嗯~不錯,是能省一行代碼。探討為什么要這么寫也沒什么意義,咱跟著國際潮流走就是了。現在已經越來越明顯,這個所謂的自定義事件,其實與其他事件是同宗同源,只是名字(類型)不同罷了。

三、自定義事件實例

  了解了這么多,你肯定也和我一樣還在困惑,上面的東西都是紙上兵法,這自定義事件到底怎么用我還是不知道。比如我就想要一個tripleclick(三擊)事件,具體該如何實現呢?下面就來實踐一下,GO~

  自定義事件的步驟我總結為“三板斧”,下面開始操練:

  ①   創建自定義事件

var e = new CustomEvent('tripleclick',{'detail':'somemsg'});//創建自定義事件tripleclick

  ② 在合適的時機觸發事件

var counter = 0;
var d1 = document.getElementById('d1');
d1.onclick = function(){
    setTimeout(function(){counter=0;},500);
    if(++counter==3){
        d1.dispatchEvent(e);
    }
}

  其實這第二步才是實現tripleclick事件的核心,首先聲明一個計數器,每次元素點擊便自增,當累計點擊三次的時候將事件派發出去,即觸發事件。為了防止每次點擊之間的間隔時間過長,每次點擊后由一個延時函數進行清零,保證只有是連續點擊才觸發。代碼不難理解。我也想在這里說說我的看法,自定義事件不單單是起個自定義名字,還要給這個事件加以描述,定義好它是在什么樣的情況下發生

  在這里,tripleclick是依賴于click的,看上去更像是一個邏輯事件,非真正的事件。但由于我們的對象確實是CustomEvent的實例,那它便無疑是一個貨真價實的自定義事件。你可能會擔心難道我們的自定義事件都要依賴于現有的事件?其實也未必,稍后會寫另外一個例子來說明。

  ③   為事件注冊監聽函數

d1.addEventListener('tripleclick',function(event){
    alert(‘我被三擊了~’);
},false);

  在此處就可以把我們定義的tripleclick光明正大的寫在addEventListener函數中了。

  全部步驟就這些,完整的代碼如下:

<div id="d1">有本事點我三次</div>

<script type="text/javascript">
var d1 = document.getElementById('d1');
d1.addEventListener('tripleclick',function(event){
    alert('我被三擊了~');
},false);

var e = new CustomEvent('tripleclick',{'detail':'somemsg'});

var counter = 0;
d1.onclick = function(){
    setTimeout(function(){counter=0;},500);
    if(++counter==3){
        d1.dispatchEvent(e);
    }
}
</script>

  你可以輕輕抖動三下手指,點擊下面這個囂張的div:

有本事點我三次

  有點感覺了吧~不過這個三擊確實有點小兒科,實際上能派上用場的概率基本為0,下面就來整點有用的東西,我們來實現一個htmlchange事件,即元素的內部html發生變化時觸發該事件。這個東西在平時或許還真能用得著。

  首先,事件的創建和監聽與上面基本一樣

var e2 = new CustomEvent('htmlchange',{'detail':'somemsg'});
d1.addEventListener('htmlchange',function(event){
    alert('檢測到html發生變化!');
},false);

  然后,我們需要采用一個定時函數來不斷檢測d1內部的html,當發現與舊值不同時便派發htmlchange事件,代碼如下:

var oldhtml = d1.innerHTML;
setInterval(function(){
        if(d1.innerHTML!==oldhtml){
            d1.dispatchEvent(e2);
       oldhtml = d1.innerHTML;
        }
},100);

  三板斧完畢,來看看效果:

我得兒意的飄~

  最慢能在0.1s內作出反應,你也可以把時間設置的更小一些。我們構想一個場景,你寫的一個子頁面A要被別人的頁面B嵌套,并且B會修改A頁面中某個元素的內部html,A在無權干涉B中代碼的情況下,就可以在自己頁面中定義一個htmlchange事件,監聽B對A的修改并作出處理。怎么樣,體會到自定義事件的威力了吧。

四、談談自定義事件的用武之地

  看了很多紙上談兵的介紹之后我就一直在想,自定義事件的研究和使用為何如此少?它的真正用處到底在哪里?難道真的是javascript中的雞肋?

  在第三節舉出的兩個實例或許能說明一點什么,至少它能幫我們擴展一下DOM事件,在當前已有的事件不能滿足需求時,可以自己定義一個來使用。其他的用途呢?此時我想到了開篇提到的觀察者模式,我們認為瀏覽器對事件的處理是一種觀察者模式,如果反過來呢,我想設計一種觀察者模式,是否可以用自定義事件來實現呢?主體需要發布的各種消息通過創建各種自定義事件來實現,對于消息的訂閱則通過注冊監聽器來實現,豈不是利用現有資源便完成了一個觀察者模式,而不必再寫那么多的代碼去模擬。到這里我又情不自禁的想到了node.js,在node中,用事件驅動來完成代碼邏輯,其事件是否跟這里的自定義事件如出一轍?只是個猜測,我對node一知半解,真相也不得而知。

  不過至少也可以得出一個結論,自定義事件并非雞肋,站在設計模式或者是設計一個框架的角度來看,它的特性或許真是處理某類問題的靈丹妙藥。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()