1)对象A直接调用对象B的某个方法,实现交互;直接方法调用本质上也是属于一种特殊的发送与接受消息,它把发送消息和接收消息合并为一个动作完成; 方法调用方和被调用方被紧密耦合在一起;因为发送消息和接收消息是在一个动作内完成,所以无法做到消息的异步发送和接收; 2)对象A生成消息->将消息通知给一个事件消息处理器(Observable)->消息处理器通过同步或异步的方式将消息传递给接收者; 这种方式是通过将消息发送和消息接收拆分为两个过程,通过一个中间者来控制消息是同步还是异步发送; 在消息通信的灵活性方面比较有优势,但是也带来了一定的复杂度。但是复杂度一般可以由框架封装,消息的发送方和接收方仍然可以做到比较简单; 总的来说就是一种松耦合的处理,2个对象之间有太多紧密的直接关联,应该要考虑通过消息通信解耦,从而提高应用程序的可维护性和重用性
在JS中,消息的通知是通过事件表达的,当代码库增长到一定的规模,就需要考虑将行为和自定义事件进行解耦。 了解自定义事件的概念
理论太抽象,看看jQuery框架中如何使用事件
案例jQuery的事件自定义事件还是通过on绑定的,然后再通过trigger来触发这个事件 //给element绑定hello事件 element.bind("hello",function(){ alert("hello world!"); }); //触发hello事件 element.trigger("hello"); 这段代码这样写似乎感觉不出它的好处,看了下面的例子也许你会明白使用自定义事件的好处了:
我们已一个选项卡的插件为例: 我们让ul列表来响应点击事件,当用户点击一个列表项时,给这个列表项添加一个名为active的类,同时将其他列表项中的active类移除, 以此同时让刚刚点击的列表对应的内容区域也添加active类。 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 $.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; };
从上面的例子我们可以看到使用自定义事件回调使得选项卡状态切换回调彼此分离,让代码变得整洁易读。 $("ul#tabs").tabs("#tabsContent");
jQuery.trigger 与 document.dispatchEvent 区分浏览器提供自定义事件接口,那么就jQuery是不是利用这个原理呢?
第一种情况:DOM-events使用jQuery触发。 触发不会处理通过addEventListener绑定的事件 这是一个问题,如果你与非jQuery代码混合jQuery代码。例如,jQuery移动模拟orientationchange事件基于窗口尺寸和大小事件但它使用jQuery触发orientationchange事件。 触发,因此不调用本机事件侦听器。
trigger的几种常见用法1.常用模拟在jQuery中,可以使用trigger()方法完成模拟操作。例如可以使用下面的代码来触发id为btn按钮的click事件。 $("#btn").trigger("click"); 这样,当页面加载完毕后,就会立刻输出想要的效果。 也可以直接用简化写法click(),来达到同样的效果: $("#btn").click(); 2.触发自定义事件trigger()方法不仅能触发浏览器支持的具有相同名称的事件,也可以触发自定义名称的事件。 例如为元素绑定一个“myClick”的事件,jQuery代码如下: $("#btn").bind("myClick", function () {
$("#test").append("<p>我的自定义事件。</p>");
});
想要触发这个事件,可以使用下面的代码来实现: $("btn").trigger("myClick"); 3.传递数据trigger(tpye[,datea])方法有两个参数,第一个参数是要触发的事件类型,第二个单数是要传递给事件处理函数的附加数据,以数组形式传递。通常可以通过传递一个参数给回调函数来区别这次事件是代码触发的还是用户触发的。 下面的是一个传递数据的例子: $("#btn").bind("myClick", function (event, message1, message2) { //获取数据 $("#test").append("p" + message1 + message2 + "</p>"); }); $("#btn").trigger("myClick",["我的自定义","事件"]); //传递两个数据 $(“#btn”).trigger(“myClick”,["我的自定义","事件"]); //传递两个数据
4.执行默认操作triger()方法触发事件后,会执行浏览器默认操作。例如: $("input").trigger("focus"); 以上代码不仅会触发为input元素绑定的focus事件,也会使input元素本身得到焦点(浏览器默认操作)。 如果只想触发绑定的focus事件,而不想执行浏览器默认操作,可以使用jQuery中另一个类似的方法-triggerHandler()方法。 $("input").triggerHandler("focus"); 该方法会触发input元素上绑定的特定事件,同时取消浏览器对此事件的默认操作,即文本框指触发绑定的focus事件,不会得到焦点。
jQuery自定义事件原理根据API,trigger支持 看看demo
按照tigger绑定的方式 $('ele').on('aaa',function(){}) $('ele').on('click',function(){}) 第一种是自定义的事件名aaa,第二种是浏览器事件click 根据trigger的API,会处理冒泡这个关键点,
trigger需要处理的问题1.模拟事件对象,用户模拟处理停止事件冒泡这个很明了,因为不是通过浏览器系统触发的,而是自动触发的,所以这个事件对象要如何处理? 2.区分事件类型,触发标准的浏览器事件 和 自定义事件名绑定的处理程序。例如:事件名称+命名空间 p4.on('click.aaa.ccc',function(e,vv,c){
console.log('p4')
})
p4.trigger('click.aaa')
所以trigger触发的时 3.模拟冒泡机制那么浏览器click类型,自然是本身支持冒泡这样的行为,通过stopPropagation阻止即可 当然一些事件,如focusin和 blur本身不冒泡,但 jQuery 为了跨浏览器一致性, jQuery 需要在这些事件上模拟了冒泡行为,jQuery要如何处理? 那么如果是自定义的aaa的事件名,又如何处理冒泡? 源码解读附上源码 trigger源码
初看trigger源码部分,真有点晕,处理的hack太多了,但是仔细规划下,无非就是解决上面提到的几点问题
1 命名空间的过滤if ( type.indexOf(".") >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } 按照规范p4.trigger('click.aaa.ccc'),'click.aaa.ccc' 就是事件+命名空间的组合 判断也挺巧妙,indexOf判断有.是索引,即存在命名空间,然后踢掉第一个事件名
2 模拟事件对象event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); 在on机制里面就分析了,其实就是jQuery.Event类了
4 返回的事件数据合集data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] );
所以data就是事件回调返回的[event,data],如果传递了数据就合并到data中
5 jQuery.event.special这个在很多地方用到,这个是用来做模拟事件的,比如提到的模拟聚焦冒泡之类的,下章再讲
6 模拟事件冒泡trigger与triggerHandler的本质区别实现在这里了 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === (elem.ownerDocument || document) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } 其实大致的手法都差不多了,无非就是遍历所有的元素节点了,排个队列出来
|
|