欢迎来我的专栏检察系列文章。
之前,我只晓得,只有当对浏览器中的元素举行点击的时刻,才会动身 click 事宜,别的的事宜也一样,须要工资的鼠标操纵。
厥后跟着进修的不断深入,才晓得本来 JS 能够写函数来掌握事宜的实行,这模样写代码才有意义。记得良久良久之前一些歹意网站,明显鼠标没有点击,却被网站强行的点击了某个链接,也许完成的体式格局就是如许的吧。
原生事宜
实在 JS 的原生事宜已做得挺好了,只是 jQuery 将其举行封装,做的更好。
关于 document.createEvent,下面是一个简朴的事宜点击的例子:
var fn = function(){
console.log('click');
}
var button = document.getElementById('#id');
button.addEventListener('click', fn);
// 点击事宜 MouseEvent
var myClick = document.createEvent('MouseEvent');
myClick.initMouseEvent('click', false, false, null);
// 实行
button.dispatchEvent(myClick); // 'click'
除了鼠标事宜,还能够自定义事宜:
// 随意自定义一个事宜 test.click
button.addEventListener('test.click', fn);
var testEvent = document.createEvent('CustomEvent');
// customEvent 也能够初始化为鼠标事宜,不一定非要自定义事宜
testEvent.initCustomEvent('test.click', false, false, null);
button.dispatchEvent(testEvent); // 'click'
JS 原生的模仿事宜,运用起来照样很轻易的,以上就是原生的。
不过 jQuery 也有本身的一套自定义事宜计划。
jQuery.trigger
jQuery.trigger
能够和 HTMLElement.dispatchEvent
事宜拿来对照,他们都是用来模仿和实行监听的事宜。
怎样运用
关于运用,则比较简朴了.trigger():
var $body = $(document.body);
// 先绑定事宜
$body.on('click', function(){
console.log('click');
})
// 实行
$body.trigger('click'); //'click'
trigger 还支撑更多的参数,一样能够自定义事宜:
$body.on('click.test', function(e, data1, data2){
console.log(data1 + '-' + data2);
})
$body.trigger('click.test', ['hello', 'world']);
trigger 源码
trigger 的源码有些简朴,由于照样要借助于 jQuery.event
来处置惩罚:
jQuery.fn.extend( {
trigger: function(type, data){
return this.each(function(){
jQuery.event.trigger(type, data, this);
})
},
// triggerHandler 处置惩罚第一个且不触发默许事宜
triggerHandler: function( type, data ) {
var elem = this[ 0 ];
if ( elem ) {
return jQuery.event.trigger( type, data, elem, true );
}
}
} );
所以 trigger 事宜的出发点又回到了 jQuery.event
。
jQuery.event.trigger
实在 trigger 和 add + handler 函数很相似,大抵都是从 data cache 中搜刮缓存,实行回调函数。须要斟酌要不要实行默许事宜,即第四个参数为 true 的状况。
jQuery.extend(jQuery.event, {
// onleyHandlers 示意不斟酌冒泡事宜
trigger: function( event, data, elem, onlyHandlers ) {
var i, cur, tmp, bubbleType, ontype, handle, special,
eventPath = [ elem || document ],
type = hasOwn.call( event, "type" ) ? event.type : event,
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
cur = tmp = elem = elem || document;
// Don't do events on text and comment nodes
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}
// 异步不争执
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
return;
}
if ( type.indexOf( "." ) > -1 ) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split( "." );
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf( ":" ) < 0 && "on" + type;
// 改装原生的 event 事宜
event = event[ jQuery.expando ] ?
event :
new jQuery.Event( type, typeof event === "object" && event );
// 推断是不是只实行当前 trigger 事宜,不冒泡
event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join( "." );
event.rnamespace = event.namespace ?
new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
null;
// Clean up the event in case it is being reused
event.result = undefined;
if ( !event.target ) {
event.target = elem;
}
// Clone any incoming data and prepend the event, creating the handler arg list
data = data == null ?
[ event ] :
jQuery.makeArray( data, [ event ] );
// Allow special events to draw outside the lines
special = jQuery.event.special[ type ] || {};
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
// 向 document 冒泡并把冒泡效果存储到 eventPath 数组中
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 );
}
}
// 按需求来实行
i = 0;
while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
event.type = i > 1 ?
bubbleType :
special.bindType || type;
// 从 data cache 中取得回调函数
handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
dataPriv.get( cur, "handle" );
if ( handle ) {
// 实行
handle.apply( cur, data );
}
// Native handler
handle = ontype && cur[ ontype ];
if ( handle && handle.apply && acceptData( cur ) ) {
event.result = handle.apply( cur, data );
if ( event.result === 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( eventPath.pop(), data ) === false ) &&
acceptData( elem ) ) {
// Call a native DOM method on the target with the same name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
// Don't re-trigger an onFOO event when we call its FOO() method
tmp = elem[ ontype ];
if ( tmp ) {
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 ( tmp ) {
elem[ ontype ] = tmp;
}
}
}
}
return event.result;
},
})
总结
在 jQuery.event.trigger
中,比较有意义的是模仿冒泡机制,大抵的思绪就是:
把当前 elem 存入数组;
查找当前 elem 的父元素,假如相符,push 到数组中,反复第一步,不然下一步;
遍历数组,从 data cache 中检察是不是绑定 type 事宜,然后顺次实行。
冒泡事宜就是就是由内向外冒泡的历程,这个历程不是很庞杂。
event 事宜应当就这么多内容吧。
参考
解密jQuery事宜中心 – 自定义设想(三)
MDN createEvent
解密jQuery事宜中心 – 模仿事宜(四)
本文在 github 上的源码地点,欢迎来 star。
欢迎来我的博客交换。