一直覺得jQuery的bind只能綁定jQuery中的事件,今天從一本書上看到jQuery的bind也能綁定自定義事件。javascript
事實上咱們能夠經過bind綁定一個自定義事件,而後再經過trigger來觸發這個事件。例如給element綁定一個hello事件,再經過trigger來觸發這個事件:html
//給element綁定hello事件 element.bind("hello",function(){ alert("hello world!"); }); //觸發hello事件 element.trigger("hello");
這段代碼這樣寫彷佛感受不出它的好處,看了下面的例子也許你會明白使用自定義事件的好處了:java
咱們已一個選項卡的插件爲例:咱們讓ul列表來響應點擊事件,當用戶點擊一個列表項時,給這個列表項添加一個名爲active的類,同時將其餘列表項中的active類移除,以此同時讓剛剛點擊的列表對應的內容區域也添加active類。node
HTML:瀏覽器
<ul id="tabs"> <li data-tab="users">Users</li> <li data-tab="groups">Groups</li> </ul> <div id="tabsContent"> <div data-tab="users">part1</div> <div data-tab="groups">part2</div> </div>
jQuery:app
$.fn.tabs=function(control){ var element=$(this); control=$(control); element.delegate("li","click",function(){ var tabName=$(this).attr("data-tab"); //點擊li的時候觸發change.tabs自定義事件 element.trigger("change.tabs",tabName); }); //給element綁定一個change.tabs自定義事件 element.bind("change.tabs",function(e,tabName){ element.find("li").removeClass("active"); element.find(">[data-tab='"+ tabName +"']").addClass("active"); }); element.bind("change.tabs",function(e,tabName){ control.find(">[data-tab]").removeClass("active"); control.find(">[data-tab='"+ tabName +"']").addClass("active"); }); //激活第一個選項卡 var firstName=element.find("li:first").attr("data-tab"); element.trigger("change.tabs",firstName); return this; };
因爲插件位於jQuery的prototype裏面,所以咱們能夠基於jQuery實例來調用:框架
$("ul#tabs").tabs("#tabsContent");
從上面的例子咱們能夠看到使用自定義事件回調使得選項卡狀態切換回調彼此分離,讓代碼變得整潔易讀。異步
javascript和jquey的自定義事件小結函數
對象之間經過直接方法調用來交互post
1)對象A直接調用對象B的某個方法,實現交互;直接方法調用本質上也是屬於一種特殊的發送與接受消息,它把發送消息和接收消息合併爲一個動做完成;
方法調用方和被調用方被緊密耦合在一塊兒;由於發送消息和接收消息是在一個動做內完成,因此沒法作到消息的異步發送和接收;
2)對象A生成消息->將消息通知給一個事件消息處理器(Observable)->消息處理器經過同步或異步的方式將消息傳遞給接收者;
這種方式是經過將消息發送和消息接收拆分爲兩個過程,經過一箇中間者來控制消息是同步仍是異步發送;
在消息通訊的靈活性方面比較有優點,可是也帶來了必定的複雜度。可是複雜度通常能夠由框架封裝,消息的發送方和接收方仍然能夠作到比較簡單;
總的來講就是一種鬆耦合的處理,2個對象之間有太多緊密的直接關聯,應該要考慮經過消息通訊解耦,從而提升應用程序的可維護性和重用性
在JS中,消息的通知是經過事件表達的,當代碼庫增加到必定的規模,就須要考慮將行爲和自定義事件進行解耦。
瞭解自定義事件的概念
1、jQuery自定義事件
jQuery的自定義事件是經過on和one綁定的,而後再經過trigger來觸發這個事件
若有三種狀況須要分別處理:
jQuery 提供的自定義事件能夠引入語義,很好地解決問題
//1. 定義自定義事件 $('#username').on('blank.username', function() { console.log('請不要留空'); }); $('#username').on('notExist.username', function() { console.log('用戶名不存在'); }); $('#username').on('success.username', function() { console.log('用戶名存在'); }); //2. 觸發自定義事件 $('.js-submit').on('click', function() { var username = $('#username').val(); username = $.trim(username); if (username === '') { $('#username').trigger('blank.username'); // 若是 username 爲空值,則觸發 blank.username 事件 } $.post(url, {username: username}, function(data) { var res = data; if (res.retcode === -1) { $('#username').trigger('notExist.username'); // 若是用戶不存在,則觸發 notExist.username 事件 } else if (res.retcode === 0) { $('#username').trigger('success.username'); // 若是用戶存在,則觸發 sucess.username 事件 } }); });
trigger須要處理的問題
1.模擬事件對象,用戶模擬處理中止事件冒泡
triger()方法觸發事件後,會執行瀏覽器默認操做。例如:
$("input").trigger("focus");
以上代碼不只會觸發爲input元素綁定的focus事件,也會使input元素自己獲得焦點(瀏覽器默認操做)。
若是隻想觸發綁定的focus事件,而不想執行瀏覽器默認操做,可使用jQuery中另外一個相似的非冒泡式方法-triggerHandler()方法。
$container.one("focus",function(){
.....
});
$("input").triggerHandler("focus");
該方法會觸發input元素上綁定的特定事件,同時取消瀏覽器對此事件的默認操做,即文本框指觸發綁定的focus事件,不會獲得焦點。
請注意這裏使用了jQuery 的one 來代替on。這二者的區別在於,one 在觸發處理器以後會自動將其刪除。
2.區分事件類型,觸發標準的瀏覽器事件 和 自定義事件名綁定的處理程序。
解決方法:事件名稱+命名空間
p4.on('click.aaa.ccc',function(e,vv,c){ console.log('p4') }) p4.trigger('click.aaa')
2、javascript的自定義事件
1. 簡單的自定義事件
自定義事件到激發這個事件,須要document.createEvent(),event.initEvent(),element.dispatchEvent()這三部,分別是建立事件對象,初始化事件對象,觸發事件
<div id="testBox"></div> // 1. 建立事件 var evt = document.createEvent('HTMLEvents'); // 2. 定義事件類型,事件初始化 evt.initEvent('customEvent', true, true); // 3. 在元素上監聽事件,綁定監聽 var obj = document.getElementById('testBox'); obj.addEventListener('customEvent', function(){ console.log('customEvent 事件觸發了'+event.type); }, false);
function foo1(){ addLog("foo1 is excute"); } function foo2(){ addLog("the id is "+idChange.getId()+" now!"); } if(document.createEvent){ //This is for the stand browser. var ev=document.createEvent('HTMLEvents'); ev.initEvent('fakeEvent',false,false); document.addEventListener("fakeEvent",foo1,false); document.addEventListener("fakeEvent",foo2,false); }else if(document.attachEvent){ //This is for the damn IE document.documentElement.fakeEvents = 0; // an expando property document.documentElement.attachEvent("onpropertychange", function(event) { if (event.propertyName == "fakeEvents") { foo1(); } }); document.documentElement.attachEvent("onpropertychange",function(event){ if(event.propertyName == "fakeEvents"){ foo2(); } }); } function addLog(log){ var logDiv=document.getElementById('log'); var p=document.createElement("p"); p.appendChild(document.createTextNode(log)); logDiv.appendChild(p); } var idChange=function(){ var id=1; return {getId:function(){return id;}, setId:function(a){ id=a; if(document.dispatchEvent) document.dispatchEvent(ev); else if(document.attachEvent) document.documentElement.fakeEvents++; //This for IE }} }();
2. 一個完整的事件機制
這個機制支持標準事件和自定義事件的監聽,移除監聽和模擬觸發操做。須要注意的是,爲了使到代碼的邏輯更加清晰,這裏約定自定義事件帶有 'custom' 的前綴(例如:customTest,customAlert),demo
/** * @description 包含事件監聽、移除和模擬事件觸發的事件機制,支持鏈式調用 * @author Kayo Lee(kayosite.com) * @create 2014-07-24 * */ (function( window, undefined ){ var Ev = window.Ev = window.$ = function(element){ return new Ev.fn.init(element); }; // Ev 對象構建 Ev.fn = Ev.prototype = { init: function(element){ this.element = (element && element.nodeType == 1)? element: document; }, /** * 添加事件監聽 * * @param {String} type 監聽的事件類型 * @param {Function} callback 回調函數 */ add: function(type, callback){ var _that = this; if(_that.element.addEventListener){ /** * @supported For Modern Browers and IE9+ */ _that.element.addEventListener(type, callback, false); } else if(_that.element.attachEvent){ /** * @supported For IE5+ */ // 自定義事件處理 if( type.indexOf('custom') != -1 ){ if( isNaN( _that.element[type] ) ){ _that.element[type] = 0; } var fnEv = function(event){ event = event ? event : window.event if( event.propertyName == type ){ callback.call(_that.element); } }; _that.element.attachEvent('onpropertychange', fnEv); // 在元素上存儲綁定的 propertychange 的回調,方便移除事件綁定 if( !_that.element['callback' + callback] ){ _that.element['callback' + callback] = fnEv; } // 標準事件處理 } else { _that.element.attachEvent('on' + type, callback); } } else { /** * @supported For Others */ _that.element['on' + type] = callback; } return _that; }, /** * 移除事件監聽 * * @param {String} type 監聽的事件類型 * @param {Function} callback 回調函數 */ remove: function(type, callback){ var _that = this; if(_that.element.removeEventListener){ /** * @supported For Modern Browers and IE9+ */ _that.element.removeEventListener(type, callback, false); } else if(_that.element.detachEvent){ /** * @supported For IE5+ */ // 自定義事件處理 if( type.indexOf('custom') != -1 ){ // 移除對相應的自定義屬性的監聽 _that.element.detachEvent('onpropertychange', _that.element['callback' + callback]); // 刪除儲存在 DOM 上的自定義事件的回調 _that.element['callback' + callback] = null; // 標準事件的處理 } else { _that.element.detachEvent('on' + type, callback); } } else { /** * @supported For Others */ _that.element['on' + type] = null; } return _that; }, /** * 模擬觸發事件 * @param {String} type 模擬觸發事件的事件類型 * @return {Object} 返回當前的 Kjs 對象 */ trigger: function(type){ var _that = this; try { // 現代瀏覽器 if(_that.element.dispatchEvent){ // 建立事件 var evt = document.createEvent('Event'); // 定義事件的類型 evt.initEvent(type, true, true); // 觸發事件 _that.element.dispatchEvent(evt); // IE } else if(_that.element.fireEvent){ if( type.indexOf('custom') != -1 ){ _that.element[type]++; } else { _that.element.fireEvent('on' + type); } } } catch(e){ }; return _that; } } Ev.fn.init.prototype = Ev.fn; })( window );