本篇来介绍下关于jQ的事件触发机制:
当我们触发绑定事件时,本质上是使用了jQuery.event.dispatch(e)方法
我们来看回顾下载on章节中,提到的绑定事件到dom元素上的代码
jQuery.event = {
//绑定事件
add: function( elem, types, handler, data, selector ) {
//...
// 没有的话,先设置一个主监听函数
if ( !eventHandle ) {
//jQ的事件系统只会为元素分配一个主监听函数,
//并且所有类型的事件在绑定时,只会绑定这个函数
elemData.handle = eventHandle = function( e ) {
return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// 将dom元素绑定到事件的监听函数上
eventHandle.elem = elem;
}
//...
}
}
可以看出, 实际绑定到dom中, 被浏览器触发的函数, 是一个将dispatch()封装过后的函数,
下面小编来show下源码:
看之前小编先声明一点,这个api里的this指向的是触发的DOM元素(注意上面的apply哦)
jQuery.event = {
//...
dispatch: function( event ) {
// 将原生的event的对象,包装为一个jQuery.Event对象
// 这里做了些兼容性处理,将一些不兼容的参数进行了修正
event = jQuery.event.fix( event || window.event );
//处理事件的事件队列
var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
//事件委托的计数器
delegateCount = handlers.delegateCount,
//传入进来的参数的数组
args = [].slice.call( arguments, 0 ),
//手动 trigger 的事件如果有 ! 号,则 exclusive 为 true,表示只执行不带命名空间的fn
//当 exclusive 为 false,并且事件没有指定命名空间时,则运行所有的事件
run_all = !event.exclusive && !event.namespace,
//查看事件是否有修正的操作对象
special = jQuery.event.special[ event.type ] || {},
//待执行队列
handlerQueue = [],
i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
//修正后的event重新设置下
args[0] = event;
//设置 事件代理 的对象
event.delegateTarget = this;
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
return;
}
// 事件委托计数器>0 || (点击事件 && 鼠标左键)
if ( delegateCount && !(event.button && event.type === "click") ) {
jqcur = jQuery(this);
jqcur.context = this.ownerDocument || this;
//这里的for的目的是 从目标元素一直遍历到自身元素,然后同时遍历每个代理对象的对象
//来匹配下对应的事件处理元素又哪些
for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
// 不触发隐藏元素的事件
if ( cur.disabled !== true ) {
selMatch = {};
matches = [];
jqcur[0] = cur;
for ( i = 0 ; i < delegateCount ; i++ ) {
handleObj = handlers[ i ];
//委托事件的匹配串
sel = handleObj.selector;
//存储dom元素是否是每个函数要处理的dom
if ( selMatch[ sel ] === undefined ) {
selMatch[ sel ] = (
//如果函数存在quick,则直接调用quickIs的匹配,否则调用$().is()的匹配api
//is可以返回元素是否可以被sel匹配上
handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
);
}
//如果元素被匹配上,则把函数追加到队列中
if ( selMatch[ sel ] ) {
matches.push( handleObj );
}
}
if ( matches.length ) {
//加入到待执行的队列中 { 触发元素, 触发函数数组 }
handlerQueue.push({ elem: cur, matches: matches });
}
}
}
}
// 如果事件队列数组的长度大于委托计数器,则将剩余的函数都追加到待触发事件的末尾
if ( handlers.length > delegateCount ) {
handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
}
// 先执行事件委托,这样可以通过 stopPropagation 来阻止事件的传播
for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
matched = handlerQueue[ i ];
//代理事件可以通过这个属性来获取代理的元素
event.currentTarget = matched.elem;
for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
handleObj = matched.matches[ j ];
// Triggered event must either
// 1) be non-exclusive and have no namespace, or
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
if( run_all ||
(!event.namespace && !handleObj.namespace) ||
event.namespace_re &&
event.namespace_re.test( handleObj.namespace )
){
//用户绑定的自定义数据
event.data = handleObj.data;
event.handleObj = handleObj;
//触发调用
ret = (
(jQuery.event.special[ handleObj.origType ] || {}).handle ||
handleObj.handler
).apply( matched.elem, args );
if ( ret !== undefined ) {
//保存运行结果
event.result = ret;
//如果返回了false,
//则调用 阻止默认事件 && 阻止事件冒泡
if ( ret === false ) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
}
// 执行特殊的触发操作
if ( special.postDispatch ) {
special.postDispatch.call( this, event );
}
return event.result;
}
}
本篇的逻辑性还是很顺序化的, 没有跳跃性, 就不多解释了。
感谢您的阅读,欢迎留言:斧正,交流,提问;