一直觉得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 );