由于浏览器事件机制的不兼容性,譬如最常见的注册事件差异 ,不常见的事件触发顺序差异 ,以及为了支撑 component infrastructure,基本上常见的类库 YUI3,jquery,extjs 都包含了 event 模块用来注册 dom 事件和实现自定义事件的绑定与触发,理想情况下都希望自定义事件的表现同 dom 事件完全兼容,毕竟javascript不能完全的代替原生代码,虽然 yui3 已经很好的统一了自定义事件与dom事件间的鸿沟,但这是在改变了原生dom事件机制的基础上,这次从一个角度来说一下这个问题。
自定义事件监听器的返回值问题
在组件内部实现中,常常要根据调用者对某些事件的反应来进行一些操作(即AOP的概念),比如:
var Component ={ run:function(){ if(this.fire("beforeRun")==false) { return; } //action } }; Component.on("beforeRun",function(){ if(checkedFalse) return false; });
其中如果调用者不同意某个操作,则在对应操作的触发前事件监听器返回false即可。但是如果存在多个触发前事件监听器,那么各个框架的表现以及与原生dom事件的监听器return false的区别就值得总结:
html结构:
Extjs:
dom 事件:
Extjs dom 事件机制直接调用原生注册函数,每个用户的回调函数 a 被包装成另外一个函数 b 用以 normalize 事件,b 函数被浏览器原生的事件注册机制使用,故和原生事件机制表现一致,return false 只用来阻止浏览器的默认行为。
Ext.get("inner").on("click",function(e){ console.log("inner click -1"); return false; }); Ext.get("inner").on("click",function(e){ console.log("inner click -2"); }); Ext.get("outer").on("click",function(e){ console.log("outer click"); });
输出:
inner click -1 inner click -2 outer click innner leaving
自定义事件:
Extjs 自定义事件机制,遵从 observer-listener 模式,fire 时如果一个自定义事件处理函数 return false,那么之后的事件处理函数不再运行,并且 fire 返回false
function test() { test.superclass.constructor.apply(this,arguments); } Ext.extend(test,Ext.util.Observable); var t=new test(); t.on("run",function(){ console.log("custom event run -1"); return false; }); t.on("run",function(){ console.log("custom event run -2"); }); console.log(t.fireEvent("run"));
输出:
custom event run -1 false
示例查看(watch console)
YUI3:
dom事件:
YUI3 将自定义事件与dom事件结合在一起,都augment自EventTarget,原理同extjs的自定义事件,故和Extjs的自定义事件表现一致。并且不会阻止冒泡以及停止默认。后面注册的监听器不再调用,但是模拟触发的返回值对于dom事件是没有意义的,simulate没有返回值
PS : return false 同 stopImmediatePropagation 不同之处在于 return false 不会阻止事件冒泡,但stopImmediatePropagation会
var inner=Y.one("#inner"); var outer=Y.one("#outer"); inner.on("click",function(e){ console.log("inner click -1"); return false; }); inner.on("click",function(e){ console.log("inner click -2"); }); outer.on("click",function(e){ console.log("outer click"); }); //Y.Event.simulate(inner._node,"click");
输出:
inner click -1 outer click innner leaving
自定义事件:
原理同dom事件,yui3不同于extjs之处在于对于自定义事件增加了模拟dom事件的冒泡(bubbleTarget),默认处理函数(defaultFn)以及自定义事件独有的广播(broadcast)。
function testCustom(){} Y.augment(testCustom,Y.EventTarget); var t=new testCustom(); t.on("run",function(){ console.log("custom event run -1"); return false; }); t.on("run",function(){ console.log("custom event run -2"); }); console.log(t.fire("run"));
输出:
custom event run -1 false
示例查看(watch console)
jquery:
jquery一切以节点为中心(或任何操作要以jquery节点作为开始),自定义事件也必须附加在节点上,触发自定义事件在节点层次中冒泡。KISSY借鉴,dom事件与自定义事件没有区别,触发的返回值为了链式操作返回自身而丧失了aop的依赖判断。
return false 会停止冒泡以及阻止默认行为,但不会阻止后续监听器运行
$("#inner").bind("click",function(){ console.log("inner click -1"); return false; }); $("#inner").bind("click",function(){ console.log("inner click -2"); }); $("#inner").bind("custom",function(){ console.log("inner custom -1"); return false; }); $("#inner").bind("custom",function(){ console.log("inner custom -2"); }); $("#outer").bind("click",function(){ console.log("outer click"); }); $("#outer").bind("custom",function(){ console.log("outer custom"); }); console.log($("#inner").trigger("click")); console.log($("#inner").trigger("custom"));
输出:
inner click -1 inner click -2 [a#inner javascri...aving');] inner custom -1 inner custom -2 [a#inner javascri...aving');]
示例查看(watch console)
总结:
return false 在原生dom事件机制中与框架自定义事件机制中存在不少的差异,原生 dom 事件为异步,触发顺序不定,而自定义事件为同步,javascript 自己实现,是否选择统一需要慎重考虑,我的建议则是采用yui3的事件机制来尽可能的统一自定义与dom事件,保持一致性,而return false 在dom事件中禁止使用,用event.preventDefault() 代替,减少使用者的疑惑。