在前几章中,多次提到过有关 special[eventType] 对象,
本章来解释下各种事件的各种修正:
先来简单回顾下关于特殊对象的出现场景:
jQuery.event = {
add: function(){
//... jQuery.event.special[ type ]
if ( !special.setup ||
special.setup.call( elem, data, namespaces, eventHandle ) === false
) {
//...
}
//...
},
remove: function(){
//... jQuery.event.special[ type ]
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
//...
},
trigger: function(){
//... jQuery.event.special[ type ]
if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
//...
},
}
好了,想必大家也应该稍有印象了,下面小编来整体介绍下有关所有的修正操作,
jQuery.event = {
/***************************************************************************
对修正对象的整体梳理:
onBubble 当前事件类型不支持或者不允许冒泡
bindType 绑定普通事件时使用的事件类型
delegateType 绑定代理事件时使用的事件类型
setup 用于执行特殊的主监听函数的“绑定行为”或者绑定前的初始化操作,
在绑定前被调用,如果返回false则继续调用原生方法绑定主监听函数
if ( !special.setup ||
special.setup.call( elem, data, namespaces, eventHandle ) === false
) {
// addEventListener || attachEvent
}
teardown 同上,这是移除前需要调用的
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
jQuery.removeEvent( elem, type, elemData.handle );
}
handle 执行事件响应行为,在每次出发当前类型的事件时调用
ret = (
(jQuery.event.special[ handleObj.origType ] || {}).handle ||
handleObj.handler
).apply( matched.elem, args );
add 在处理事件绑定时被调用,该方法触发后,会继续执行正常的绑定流程
if ( special.add ) {
special.add.call( elem, handleObj );
}
remove 在删除事件前调用
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
trigger 触发当前类型事件时被调用
if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
return;
}
_default 执行特殊的默认行为,如果返回false则执行浏览器默认行为
if(
(!special._default ||
special._default.apply( elem.ownerDocument, data ) === false
) &&
//....
){}
***************************************************************************/
special: {
ready: {
// 这个事件总也不会返回false,对这个函数各位应该是很眼熟了,就不多解释了,
// 不太清楚的可以参考小编写的第一篇
setup: jQuery.bindReady
},
load: {
// 该事件不允许冒泡
// 实际上浏览器本身是不会冒泡的,但是在手动触发该事件时,
// 在构造冒泡路径时,会检测这个属性,来判断是否执行冒泡模拟
noBubble: true
},
// focus/blur浏览器是不会冒泡的,
// 所以当用户要绑定事件代理时,换用focusin/focusout
focus: {
delegateType: "focusin"
},
blur: {
delegateType: "focusout"
},
//beforeunload在页面刷新或关闭时触发
//这个事件如果使用addEvent||attach进行绑定的话,ie9以下和ff中是不会被触发的
//以下两种形式都可以跨浏览器的解决
// <body onbeforeunload="handler">
// window.onbeforeunload = handler;
beforeunload: {
setup: function( data, namespaces, eventHandle ) {
if ( jQuery.isWindow( this ) ) {
this.onbeforeunload = eventHandle;
}
},
teardown: function( namespaces, eventHandle ) {
if ( this.onbeforeunload === eventHandle ) {
this.onbeforeunload = null;
}
}
}
},
//...
}
// 初始化事件对应的修正: mouseenter, mouseleave
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
delegateType: fix,
bindType: fix,
handle: function( event ) {
var target = this,
//目标元素的关联元素
related = event.relatedTarget,
//事件处理对象
handleObj = event.handleObj,
//事件代理的字符串
selector = handleObj.selector,
ret;
// jQuery.contains(a,b) 函数的作用是检测a是否包含b,包含就返回true,不包含就返回false
// jQ通过检测 目标元素 和 关联元素 的关系,来决定是否触发over||out事情
// 最终的效果是:
// 当从父元素移入到子元素时,不触发 父元素的mouseout事件 和 子元素冒泡上来的mouseover事件
// 当从子元素移入到父元素时,不触发 父元素的mouseover事件 和 子元素冒泡到父元素的mouseout事件
if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
event.type = handleObj.origType;
ret = handleObj.handler.apply( this, arguments );
event.type = fix;
}
return ret;
}
};
});
// 在ie6,7,8下submit事件是不支持冒泡的,这里通过模拟来实现
if ( !jQuery.support.submitBubbles ) {
jQuery.event.special.submit = {
//绑定dom前被执行..
setup: function() {
// 如果当前元素是表单元素,就当成普通事件帮就可以了
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// 为当前元素绑定两个事件:带命名空间 "_submit" 的,
jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
var elem = e.target,
form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
//如果元素还没有绑定过 submit._submit 事件,则绑定一次
if ( form && !form._submit_attached ) {
jQuery.event.add( form, "submit._submit", function( event ) {
event._submit_bubble = true;
});
form._submit_attached = true;
}
});
//没有返回,false,执行后续的正常的绑定逻辑
},
//事件触发后执行
postDispatch: function( event ) {
if ( event._submit_bubble ) {
delete event._submit_bubble;
if ( this.parentNode && !event.isTrigger ) {
//模拟事件submit,这里可以暂时理解为模拟了事件冒泡,具体的下章介绍
jQuery.event.simulate( "submit", this.parentNode, event, true );
}
}
},
//卸载事件前调用
teardown: function() {
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
//删除_submit空间下的所有事件
jQuery.event.remove( this, "._submit" );
}
};
}
// 在ie6,7,8下,change事件是不支持冒泡的,
if ( !jQuery.support.changeBubbles ) {
jQuery.event.special.change = {
setup: function() {
//rformElems = /^(?:textarea|input|select)$/i
//元素是 textarea input select 时,不需要代理,只需执行正常绑定即可
if ( rformElems.test( this.nodeName ) ) {
//在ie6,7,8下checkbox和radio要等到失去焦点时才会触发hange,
//其他浏览器在点击的时候就会触发,所以这里通过模拟来进行触发
if ( this.type === "checkbox" || this.type === "radio" ) {
jQuery.event.add( this, "propertychange._change", function( event ) {
//这里进行检测的原因是因为如果使用setAttribute()时,也会触发改事件,
//而这里对设置的属性值进行检测,当为checked时,执行即可
//这个会在click之前触发
if ( event.originalEvent.propertyName === "checked" ){
this._just_changed = true;
}
});
jQuery.event.add( this, "click._change", function( event ){
//触发点击事件时,如果是发生了对元素的点击,并且不是手动触发,
//则模拟change事件
if ( this._just_changed && !event.isTrigger ){
this._just_changed = false;
jQuery.event.simulate( "change", this, event, true );
}
});
}
//其他匹配成功的元素就不做操作了,返回即可
return false;
}
//当鼠标按下的时候会触发,并且是在元素获得焦点前,改事件支持冒泡,ie支持
jQuery.event.add( this, "beforeactivate._change", function( e ) {
var targetElem = e.target;
//在获取焦点前,如果事件源是表单元素,则为其绑定change事件
if ( rformElems.test( targetElem.nodeName ) && !targetElem._change_attached ) {
jQuery.event.add( targetElem, "change._change", function( event ) {
//这个事件在被触发时,将模拟change事件
if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
jQuery.event.simulate( "change", this.parentNode, event, true );
}
});
//目标元素设定为true,以后不必再绑了
targetElem._change_attached = true;
}
});
},
//触发事件时调用
handle: function( event ) {
var elem = event.target;
//
if( this !== elem ||
event.isSimulated ||
event.isTrigger ||
(elem.type !== "radio" && elem.type !== "checkbox")
)
//触发真实事件
return event.handleObj.handler.apply( this, arguments );
},
teardown: function() {
jQuery.event.remove( this, "._change" );
return rformElems.test( this.nodeName );
}
};
}
// focusin,focusout支持的并不完善,所以这里使用了模拟的方式来
if ( !jQuery.support.focusinBubbles ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
var attaches = 0,
//主监听函数,在捕捉阶段被触发,手动模拟冒泡情况
handler = function( event ) {
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
};
jQuery.event.special[ fix ] = {
setup: function() {
//这里会一直递增,但是只会在最初绑定一次事件
if ( attaches++ === 0 ) {
//第三个参数绑定的是true哦
document.addEventListener( orig, handler, true );
}
},
// 当绑定或代理的事件全部被移除后,
// 再移除document上的focus,blur事件绑定的捕捉阶段的主监听函数
teardown: function() {
//这里会一直递减,但只会在最后一次解除绑定函数
if ( --attaches === 0 ) {
document.removeEventListener( orig, handler, true );
}
}
};
});
}
本篇就介绍到这里。
感谢您的阅读,欢迎留言:斧正,交流,提问;