说起事件event,跟通知notification总是有想通相似之处的,然而又跟消息message不一样。
几年前学习as3的时候就有过这些疑问,参见as3事件处理机制的一些缺陷
message可以认为是1对1或者1对n的有明确目标的沟通,有明确的关注目标。而event、notification则不一样,是一种观察者模式的东西,发出event、notification的主体不知道也不关心外部有没有人在观察到或者给出回应。
js的event跟as的event一样,可以通过冒泡来达到跨层级侦听的目的,其他的跟notification没有差异。都是类似观察者模式,侦听器就是观察者。
1、对于没有event的语言,可以用回调来实现notification。不同的notification用不同的字符串标示映射到不同的回调。
2、对于有event的语言,可以用event也可以用回调来实现notification。
譬如(如:flash里的PureMVC)可以用一个全局侦听器(EventDispatcher(js里叫EventTarget)支持event分发、侦听的object)来分发、侦听event,实现notification。
扯远了,回归主题,js里的event如何触发。
js里只有EventTarget这种类型的object(通常只有html里的各个element继承自他)可以分发、侦听event。
方法1:dispatchEvent的方式,先new一个Event,然后由EventTarget dispatch。
var btn = document.getElementById('btn');
var evt = document.createEvent('MouseEvent');
//evt.initEvent('click', true, true);
evt.initMouseEvent('click', true, true, document.defaultView, 0, 100, 100, 100, 100,false, false, false, false, 0, null);
//MouseEvent.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg, screenXArg, screenYArg, clientXArg, clientYArg, ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, buttonArg, relatedTargetArg)
btn.dispatchEvent(evt);
方法2:直接调用事件名的方法来触发事件,不是所有事件都适合
// 下面这个注释拷贝自jquery的trigger源码
// Call a native DOM method on the target with the same name name as the event.
var btn = document.getElementById('btn');
btn.click();
原生的event触发就只有这两种方式了,剩下都是Notification(或者称之为模拟的event)。
jquery里的event是notification,但它模拟的比较比较好,对于eventTarget(htmlelement)分发的event支持冒泡等Event特性,对于自定义的由非EventTarget类型object分发的event则就是简单的notification。
jquery里用回调的方式来实现了notification以及自己的事件侦听机制,同时用方法2的方式来保证原生event的侦听响应。
参考前面所说,也可以用全局侦听器来实现notification,自己去想怎么实现吧。
下面是一段jquery的trigger源码,留存:
// Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
eventPath = [[ elem, special.bindType || type ]];
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
bubbleType = special.delegateType || type;
cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
for ( old = elem; cur; cur = cur.parentNode ) {
eventPath.push([ cur, bubbleType ]);
old = cur;
}
// Only add window if we got to document (e.g., not plain obj or detached DOM)
if ( old === (elem.ownerDocument || document) ) {
eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
}
}
// Fire handlers on the event path
for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
cur = eventPath[i][0];
event.type = eventPath[i][1];
handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
if ( handle ) {
handle.apply( cur, data );
}
// Note that this is a bare JS function and not a jQuery handler
handle = ontype && cur[ ontype ];
if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
event.preventDefault();
}
}
event.type = type;
// If nobody prevented the default action, do it now
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
// Call a native DOM method on the target with the same name name as the event.
// Can't use an .isFunction() check here because IE6/7 fails that test.
// Don't do default actions on window, that's where global variables be (#6170)
// IE<9 dies on focus/blur to hidden element (#1486)
if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
// Don't re-trigger an onFOO event when we call its FOO() method
old = elem[ ontype ];
if ( old ) {
elem[ ontype ] = null;
}
// Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
elem[ type ]();
jQuery.event.triggered = undefined;
if ( old ) {
elem[ ontype ] = old;
}
}
}
}