詳解javascript實(shí)現(xiàn)自定義事件
我們平時(shí)在操作dom時(shí)候經(jīng)常會(huì)用到onclick,onmouseover等一系列瀏覽器特定行為的事件,
那么自定義事件,顧名思義,就是自己定義事件類(lèi)型,自己定義事件處理函數(shù),在合適的時(shí)候需要哪個(gè)事件類(lèi)型,就去調(diào)用哪個(gè)處理程序
1.js所支持的瀏覽器默認(rèn)事件
瀏覽器特定行為的事件,或者叫系統(tǒng)事件,js默認(rèn)事件等等都行,大家知道我指的什么就行,下文我叫他js默認(rèn)事件。
js默認(rèn)事件的事件綁定,事件移出等一系列操作,相信大家都有用到過(guò),如:
//DOM0級(jí)事件處理程序 var oDiv = document.getElementById('oDiv'); oDiv.onclick = function(){ alert("你點(diǎn)擊了我"); }
又或者
//DOM2級(jí)事件處理程序 var oDiv = document.getElementById('oDiv'); //非ie oDiv.addEventListener("click",function(){ alert("你點(diǎn)擊了我"); },false); //ie oDiv.attachEvent("onclick", function(){ alert("你點(diǎn)擊了我"); });
所有我就不做過(guò)多的研究,畢竟我們來(lái)討論js自定義事件,這里給出一個(gè)我之前封裝過(guò)的處理js默認(rèn)事件的代碼:
//跨瀏覽器的事件處理程序 //調(diào)用時(shí)候直接用domEvent.addEvent( , , );直接調(diào)用 //使用時(shí)候,先用addEvent添加事件,然后在handleFun里面直接寫(xiě)其他函數(shù)方法,如getEvent; //addEventListener和attachEvent---都是dom2級(jí)事件處理程序 var domEvent = { //element:dom對(duì)象,event:待處理的事件,handleFun:處理函數(shù) //事件名稱(chēng),不含“on”,比如“click”、“mouseover”、“keydown”等 addEvent:function(element,event,handleFun){ //addEventListener----應(yīng)用于mozilla if(element.addEventListener){ element.addEventListener(event,handleFun,false); }//attachEvent----應(yīng)用于IE else if(element.attachEvent){ element.attachEvent("on"+event,handleFun); }//其他的選擇dom0級(jí)事件處理程序 else{ //element.onclick===element["on"+event]; element["on"+event] = handleFun; } }, //事件名稱(chēng),含“on”,比如“onclick”、“onmouseover”、“onkeydown”等 removeEvent:function(element,event,handleFun){ //removeEventListener----應(yīng)用于mozilla if (element.removeEventListener) { element.removeEventListener(event,handleFun,false); }//detachEvent----應(yīng)用于IE else if (element.detachEvent) { element.detachEvent("on"+event,handleFun); }//其他的選擇dom0級(jí)事件處理程序 else { element["on"+event] = null; } }, //阻止事件冒泡 stopPropagation:function(event){ if(event.stopPropagation){ event.stopPropagation(); }else{ event.cancelBubble = true;//IE阻止事件冒泡,true代表阻止 } }, //阻止事件默認(rèn)行為 preventDefault:function(event){ if(event.preventDefault){ event.preventDefault(); }else{ event.returnValue = false;//IE阻止事件冒泡,false代表阻止 } }, //獲得事件元素 //event.target--非IE //event.srcElement--IE getElement:function(event){ return event.target || event.srcElement; }, //獲得事件 getEvent:function(event){ return event? event : window.event; }, //獲得事件類(lèi)型 getType:function(event){ return event.type; } };
接下類(lèi)我們不如正題,js自定義事件
2.對(duì)象直接量封裝js自定義事件
根據(jù)上面的封裝,我們可以這樣構(gòu)思
var eventTarget = { addEvent: function(){ //添加事件 }, fireEvent: function(){ //觸發(fā)事件 }, removeEvent: function(){ //移除事件 } };
相信這樣大家還是比較好理解的,然后又有一個(gè)問(wèn)題大家可以想到,那就是,js默認(rèn)事件,js可以一一對(duì)應(yīng),知道那個(gè)是那個(gè),那么我們的自定義事件呢,這個(gè)一一對(duì)應(yīng)的映射表只能我們自己去建立,然后我這樣
var eventTarget = { //保存映射 handlers:{}, addEvent: function(){ //處理代碼 }, fireEvent: function(){ //觸發(fā)代碼 }, removeEvent: function(){ //移出代碼 } };
我是這樣構(gòu)建這個(gè)映射關(guān)系的
handlers = { "type1":[ "fun1", "fun2", // "..." ], "type2":[ "fun1", "fun2" // "..." ] //"..." }
這樣每一個(gè)類(lèi)型可以有多個(gè)處理函數(shù),以便于我們以后擴(kuò)充
接下來(lái)就是代碼方面的實(shí)戰(zhàn)的,編寫(xiě)具體的處理代碼了…
相信大家對(duì)于這個(gè)思路已經(jīng)很清楚了,我直接附上代碼
//直接量處理js自定義事件 var eventTarget = { //保存事件類(lèi)型,處理函數(shù)數(shù)組映射 handlers:{}, //注冊(cè)給定類(lèi)型的事件處理程序, //type -> 自定義事件類(lèi)型, handler -> 自定義事件回調(diào)函數(shù) addEvent: function(type, handler){ //判斷事件處理數(shù)組是否有該類(lèi)型事件 if(eventTarget.handlers[type] == undefined){ eventTarget.handlers[type] = []; } //將處理事件push到事件處理數(shù)組里面 eventTarget.handlers[type].push(handler); }, //觸發(fā)一個(gè)事件 //event -> 為一個(gè)js對(duì)象,屬性中至少包含type屬性, //因?yàn)轭?lèi)型是必須的,其次可以傳一些處理函數(shù)需要的其他變量參數(shù)。(這也是為什么要傳js對(duì)象的原因) fireEvent: function(event){ //判斷是否存在該事件類(lèi)型 if(eventTarget.handlers[event.type] instanceof Array){ var _handler = eventTarget.handlers[event.type]; //在同一個(gè)事件類(lèi)型下的可能存在多種處理事件,找出本次需要處理的事件 for(var i = 0; i < _handler.length; i++){ //執(zhí)行觸發(fā) _handler[i](event); } } }, //注銷(xiāo)事件 //type -> 自定義事件類(lèi)型, handler -> 自定義事件回調(diào)函數(shù) removeEvent: function(type, handler){ if(eventTarget.handlers[type] instanceof Array){ var _handler = eventTarget.handlers[type]; //在同一個(gè)事件類(lèi)型下的可能存在多種處理事件,找出本次需要處理的事件 for(var i = 0; i < _handler.length; i++){ //找出本次需要處理的事件下標(biāo) if(_handler[i] == handler){ break; } } //刪除處理事件 _handler.splice(i, 1); } } };
這是一種調(diào)用運(yùn)行的方法
eventTarget.addEvent("eat",function(){ console.log(123); //123 }); eventTarget.fireEvent({type: "eat"});
這種方法有一個(gè)缺點(diǎn),不能刪除該處理事件,因?yàn)槲覀兪怯糜成浔碜龅模乙膊惶岢?,直接給映射表里面存這么多數(shù)據(jù),有點(diǎn)多。
另一種方法,將處理事件提取出來(lái)(推薦)
function b(){ console.log(123); } eventTarget.addEvent("eat",b); eventTarget.fireEvent({ type: "eat" }); //123 eventTarget.removeEvent("eat",b); eventTarget.fireEvent({type: "eat"}); //空
也可以這樣,傳遞更多的參數(shù)
eventTarget.fireEvent({ type: "eat", food: "banana" }); function b(data){ console.log(data.food); //banana }
總結(jié):字面量這種方法,有點(diǎn)兒缺點(diǎn),就是萬(wàn)一一不小心,把某個(gè)屬性在handler函數(shù)里面,賦值null,這樣會(huì)造成我們的的eventTarget 方法崩盤(pán)??磥?lái)原型應(yīng)該是個(gè)好方法,更安全一點(diǎn)。
3.對(duì)象原型封裝js自定義事件
由于前面思路基本都講清楚了,這里我直接附上代碼,大家可以研究下其中的利弊,或許你可以找到更好的方法解決Ta…
//自定義事件構(gòu)造函數(shù) function EventTarget(){ //事件處理程序數(shù)組集合 this.handlers = {}; } //自定義事件的原型對(duì)象 EventTarget.prototype = { //設(shè)置原型構(gòu)造函數(shù)鏈 constructor: EventTarget, //注冊(cè)給定類(lèi)型的事件處理程序, //type -> 自定義事件類(lèi)型, handler -> 自定義事件回調(diào)函數(shù) addEvent: function(type, handler){ //判斷事件處理數(shù)組是否有該類(lèi)型事件 if(typeof this.handlers[type] == 'undefined'){ this.handlers[type] = []; } //將處理事件push到事件處理數(shù)組里面 this.handlers[type].push(handler); }, //觸發(fā)一個(gè)事件 //event -> 為一個(gè)js對(duì)象,屬性中至少包含type屬性, //因?yàn)轭?lèi)型是必須的,其次可以傳一些處理函數(shù)需要的其他變量參數(shù)。(這也是為什么要傳js對(duì)象的原因) fireEvent: function(event){ //模擬真實(shí)事件的event if(!event.target){ event.target = this; } //判斷是否存在該事件類(lèi)型 if(this.handlers[event.type] instanceof Array){ var handlers = this.handlers[event.type]; //在同一個(gè)事件類(lèi)型下的可能存在多種處理事件,找出本次需要處理的事件 for(var i = 0; i < handlers.length; i++){ //執(zhí)行觸發(fā) handlers[i](event); } } }, //注銷(xiāo)事件 //type -> 自定義事件類(lèi)型, handler -> 自定義事件回調(diào)函數(shù) removeEvent: function(type, handler){ //判斷是否存在該事件類(lèi)型 if(this.handlers[type] instanceof Array){ var handlers = this.handlers[type]; //在同一個(gè)事件類(lèi)型下的可能存在多種處理事件 for(var i = 0; i < handlers.length; i++){ //找出本次需要處理的事件下標(biāo) if(handlers[i] == handler){ break; } } //從事件處理數(shù)組里面刪除 handlers.splice(i, 1); } } };
調(diào)用方法
function b(){ console.log(123); } var target = new EventTarget(); target.addEvent("eat", b); target.fireEvent({ type: "eat" }); //123
原型這種方法,與直接量方法功能是一樣的…
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
淺談js中的attributes和Attribute的用法與區(qū)別
這篇文章主要介紹了淺談js中的attributes和Attribute的用法與區(qū)別,attributes可以獲取一個(gè)對(duì)象中的一個(gè)屬性,attributes 屬性返回指定節(jié)點(diǎn)屬性的集合,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07js 監(jiān)控iframe URL的變化實(shí)例代碼
下面小編就為大家?guī)?lái)一篇js 監(jiān)控iframe URL的變化實(shí)例代碼。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07讓getElementsByName適應(yīng)IE和firefox的方法
讓getElementsByName適應(yīng)IE和firefox的方法...2007-09-09JavaScript實(shí)現(xiàn)有限狀態(tài)機(jī)的示例代碼
有限狀態(tài)機(jī)(Finite?State?Machine,?FSM)是一種數(shù)學(xué)模型,用于描述系統(tǒng)在不同狀態(tài)下的行為,本文給大家介紹JavaScript實(shí)現(xiàn)有限狀態(tài)機(jī)的示例代碼,感興趣的朋友一起看看吧2024-12-12javascript面向?qū)ο笕筇卣髦庋b實(shí)例詳解
這篇文章主要介紹了javascript面向?qū)ο笕筇卣髦庋b,簡(jiǎn)單描述了封裝的基本概念、原理,并結(jié)合實(shí)例形式詳細(xì)分析了javascript面向?qū)ο蟪绦蛟O(shè)計(jì)中封裝的用法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-07-07JS實(shí)現(xiàn)帶有抽屜效果的產(chǎn)品類(lèi)網(wǎng)站多級(jí)導(dǎo)航菜單代碼
這篇文章主要介紹了JS實(shí)現(xiàn)帶有抽屜效果的產(chǎn)品類(lèi)網(wǎng)站多級(jí)導(dǎo)航菜單代碼,涉及JavaScript動(dòng)態(tài)操作頁(yè)面元素屬性的技巧,整體界面效果美觀大方,具有極強(qiáng)的立體感,需要的朋友可以參考下2015-09-09小程序簡(jiǎn)單兩欄瀑布流效果的實(shí)現(xiàn)
這篇文章主要介紹了小程序簡(jiǎn)單兩欄瀑布流效果的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12