JavaScript 自定義事件之我見
事件
技術(shù)一般水平有限,有什么錯(cuò)的地方,望大家指正。
事件就是用戶和瀏覽器交互的一種途徑。假如一個(gè)用戶注冊(cè)的功能,我們?cè)谔顚懲昊拘畔⒅螅c(diǎn)擊提交按鈕就可以實(shí)現(xiàn)注冊(cè)功能,要想完成這個(gè)功能所需要的就是點(diǎn)擊事件。我們預(yù)先定義好操作行為,在用戶點(diǎn)擊提交按鈕時(shí)就執(zhí)行我們預(yù)先定好的行為,在本例中我們的代碼邏輯一般就是收集用戶填寫信息,驗(yàn)證信息合法性,利用AJAX與服務(wù)器交互。
這個(gè)過程就好像我們平時(shí)封裝函數(shù)然后調(diào)用函數(shù)一樣,事件其實(shí)也就類似函數(shù)定義函數(shù)調(diào)用這樣的一個(gè)過程,只不過事件函數(shù)的調(diào)用是由用戶的一些操作來告知瀏覽器,讓瀏覽器在去調(diào)用函數(shù)的。
首先瀏覽器已經(jīng)給我們提供了一列的事件,包括click,keydown等等,為什么還需要自定義事件呢?其實(shí)就是對(duì)我們的行為進(jìn)行更準(zhǔn)確的描述。以上面的用戶注冊(cè)為例我們可以定義一個(gè)名為saveMessage的事件,在點(diǎn)擊提交按鈕時(shí)觸發(fā)這個(gè)事件,好像看起來更加直觀一些,不過這看起來和普通的函數(shù)調(diào)用沒什么區(qū)別,仔細(xì)想了想函數(shù)調(diào)用和事件觸發(fā)的區(qū)別就是由我們自己執(zhí)行的函數(shù)就是函數(shù)調(diào)用,不是由我們執(zhí)行的函數(shù)就是事件觸發(fā)??聪旅娴拇a:
window.onload = function(){ var demo = document.getElementById("demo"); demo.onclick = handler; function handler(){ console.log("aaa"); } }
在我們點(diǎn)擊按鈕的時(shí)候就會(huì)打印aaa,而且很明顯的可以看出函數(shù)并不是由我們調(diào)用的而是由瀏覽器來執(zhí)行的,如果我們直接調(diào)用函數(shù)handler()一樣可以打印aaa但這是由我們調(diào)用的所以是函數(shù)調(diào)用。
自定義事件的作用
自定義事件就是我們按照瀏覽器對(duì)事件的機(jī)制來自定義的函數(shù)。自定義事件,可以對(duì)我們的處理函數(shù)帶來更好的說明,也可以為我們的插件帶來更好的處理流程。假如我們又一個(gè)這樣的需求:從服務(wù)器端拉取一組數(shù)據(jù)然后在HTML中顯示成列表,然后標(biāo)識(shí)出第一條數(shù)據(jù),假如我們利用一個(gè)現(xiàn)有的處理函數(shù),我們可能會(huì)這樣來寫:
dataTable("url"); $("table").find("input[type='checkbox']:first").prop("checked",true);
這是不能達(dá)到我們目的的因?yàn)镴S是單線程的而AJAX是異步的,當(dāng)代碼$("table").find("input[type='checkbox']:first").prop("checked",true)執(zhí)行的時(shí)候,我們需要的數(shù)據(jù)還沒有獲取到。我們?nèi)バ薷牟寮膬?nèi)部實(shí)現(xiàn)顯然是不明智的,一個(gè)可以被人接受的插件必然是有合理的回調(diào)函數(shù)(或者自定義事件)的,假如現(xiàn)在有一個(gè)列表繪制成功的回調(diào)函數(shù),我們就可以把這個(gè)回調(diào)函數(shù)看做是一個(gè)事件,我們可以對(duì)這個(gè)事件添加事件操作,定義好處理函數(shù),然后在列表繪制成功時(shí)讓插件來執(zhí)行這個(gè)處理函數(shù)。
自定義事件實(shí)現(xiàn)
我們模擬瀏覽器原生的事件來實(shí)現(xiàn)自定義事件(en:自定義事件名稱,fn:事件處理函數(shù),addEvent:為DOM元素添加自定義事件,triggerEvent:觸發(fā)自定義事件):
window.onload = function(){ var demo = document.getElementById("demo"); demo.addEvent("test",function(){console.log("handler1")}); demo.addEvent("test",function(){console.log("handler2")}); demo.onclick = function(){ this.triggerEvent("test"); } } Element.prototype.addEvent = function(en,fn){ this.pools = this.pools || {}; if(en in this.pools){ this.pools[en].push(fn); }else{ this.pools[en] = []; this.pools[en].push(fn); } } Element.prototype.triggerEvent = function(en){ if(en in this.pools){ var fns = this.pools[en]; for(var i=0,il=fns.length;i<il;i++){ fns[i](); } }else{ return; } }
由我們自己執(zhí)行的函數(shù)是函數(shù)調(diào)用,非我們執(zhí)行的函數(shù)我們可以叫做觸發(fā)事件,既然函數(shù)不是由我們調(diào)用的,那么調(diào)用者怎樣知道調(diào)用哪些函數(shù)就是一個(gè)問題了,所以就需要在添加事件函數(shù)和觸發(fā)事件函數(shù)之間加上一些約束了,那就是兩者之間有一個(gè)都能訪問到的事件池,添加事件時(shí)把事件及對(duì)應(yīng)的處理函數(shù)放在這個(gè)池子里,當(dāng)滿足觸發(fā)條件時(shí)就去池子里找到要觸發(fā)的事件,執(zhí)行對(duì)應(yīng)的處理函數(shù),所以就有了我們上面的那一段代碼。
對(duì)同一個(gè)功能(事件)可能有很多個(gè)處理函數(shù),所以我們就需要一個(gè)集合去存儲(chǔ)這些處理函數(shù),這時(shí)我們應(yīng)該反映出兩個(gè)方案JSON或者數(shù)組,JSON的結(jié)構(gòu)是key:value,對(duì)于處理函數(shù)來說名字是沒有什么作用的所以我們用數(shù)組來保存處理函數(shù),這組函數(shù)是處理什么功能的,所以我們還需要對(duì)這組處理函數(shù)由一個(gè)說明這時(shí)候就需要JSON了-->{eventName:[]}。
以簡化的BootStrap模態(tài)窗來演示自定義事件的作用:
window.onload = function(){ var show = document.getElementById("show"); var hide = document.getElementById("hide"); var content = document.getElementById("content"); show.onclick = function(){ content.modal("show"); } hide.onclick = function(){ content.modal("hide"); } content.addEvent("show",function(){alert("show before")}); content.addEvent("shown",function(){ document.getElementById("input").focus(); alert("show after"); }); } ;(function(ep){ ep.addEvent = function(en,fn){ this.pools = this.pools || {}; if(en in this.pools){ this.pools[en].push(fn); }else{ this.pools[en] = []; this.pools[en].push(fn); } } ep.triggerEvent = function(en){ if(en in this.pools){ var fns = this.pools[en]; for(var i=0,il=fns.length;i<il;i++){ fns[i](); } }else{ return; } } ep.modal = function(t){ switch(t){ case "show": this.triggerEvent("show"); this.style.display = "block"; setTimeout(function(){this.triggerEvent("shown")}.bind(this),0);//該定時(shí)器主要是為了在視覺上先看見content,在彈出消息 break; case "hide": this.style.display = "none"; break; default: break; } } }(Element.prototype));
我們可以預(yù)先定義好在彈窗出現(xiàn)之前和出現(xiàn)之后的處理函數(shù),當(dāng)彈窗觸發(fā)對(duì)應(yīng)事件的時(shí)候就執(zhí)行對(duì)應(yīng)的處理函數(shù)。
相關(guān)文章
JS幾個(gè)常用的函數(shù)和對(duì)象定義與用法示例
這篇文章主要介紹了JS幾個(gè)常用的函數(shù)和對(duì)象定義與用法,涉及JavaScript日期時(shí)間、數(shù)值計(jì)算及變量判斷等相關(guān)操作技巧,需要的朋友可以參考下2020-01-01原型方法的不同寫法居然會(huì)影響調(diào)試的解決方法
原型方法的不同寫法居然會(huì)影響調(diào)試的解決方法...2007-03-03three.js實(shí)現(xiàn)3D影院的原理的代碼分析
本篇文章主要給大家講解了如何通過three.js實(shí)現(xiàn)3D影院的功能以及原理分析,需要的朋友參考一下吧。2017-12-12在JavaScript中構(gòu)建ArrayList示例代碼
這篇文章主要介紹了在JavaScript中構(gòu)建ArrayList,很實(shí)用,需要的朋友可以參考下2014-09-09JavaScript實(shí)現(xiàn)圖像壓縮的方法
使用 JavaScript 和 canvas 壓縮圖像可以使用 canvas 的 drawImage() 方法將圖像繪制到 canvas 上,然后使用 toDataURL() 方法將圖像轉(zhuǎn)換為 Data URL 形式,這篇文章主要介紹了JavaScript 圖像壓縮的相關(guān)資料,需要的朋友可以參考下2023-01-01