本篇带来的是jQ对原生事件的包装,
先来看个栗子
$(document).bind('click',function(e){
var e = e || window.event,
tar = e.target || e.srcElement;
alert(tar);
});
上面这段代码看起来似乎没什么问题,也考虑到的兼容性的event对象,
但其实,是有问题的:根本不用进行兼容性检测.
使用jQ绑定的事件,接受的e参数是被处理过的,并非浏览器基于的原生event,
下面我们来看下jQ对于event是肿么样的处理:
首先浏览器触发了事件,jQ捆绑在dom上的函数接受到了原生的event对象,
然后交给jQuery.event.fix(),这里处理了兼容性的差异,
下一步又交给了jQuery.Event(),这里会返回一个jQ对象,
然后又回到jQuery.event.fix(),将原生事件的一些属性绑定到jQ对象,并处理兼容性属性问题.
然后出发真正的用户绑定上的函数,将处理好的event对象传递进去。
下面的代码,小编建议先从fix()函数看起。
jQuery.event = {
//...
//事件对象的公共属性
props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
//键盘事件对象的属性和修正方法
keyHooks: {
props: "char charCode key keyCode".split(" "),
filter: function( event, original ) {
// 关于上面这4个属性,兼容性到ie6的是keyCode,其他的都是ie9+支持
if ( event.which == null ) {
event.which = original.charCode != null ? original.charCode : original.keyCode;
}
return event;
}
},
//鼠标事件对象的属性和修正方法
mouseHooks: {
props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
filter: function( event, original ) {
var eventDoc, doc, body,
button = original.button,
fromElement = original.fromElement;
if ( event.pageX == null && original.clientX != null ) {
eventDoc = event.target.ownerDocument || document;
doc = eventDoc.documentElement;
body = eventDoc.body;
//pageX,pageY 表示距离文档左上角的坐标,这个属性ie9-不支持
//jQ进行了响应的计算
event.pageX = original.clientX +
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
( doc && doc.clientLeft || body && body.clientLeft || 0 );
event.pageY = original.clientY +
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
( doc && doc.clientTop || body && body.clientTop || 0 );
}
//relatedTarget属性指的是和目标元素相关的元素对象
//鼠标离开事件:目标元素是离开的元素, 相关元素是离开后进入到的元素
//鼠标移入事件:目标元素是进入的元素, 相关元素是从哪个元素离开的那个元素
if ( !event.relatedTarget && fromElement ) {
event.relatedTarget = fromElement === event.target ?
original.toElement :
fromElement;
}
// ie低版本浏览器中, 没有which属性,button的值是142,然后根据button的值对which进行修正
// 修正后的which的值是 1 2 3
// 关于鼠标点击比较乱,建议大家以后使用jQ这种修正后的which属性
if ( !event.which && button !== undefined ) {
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
}
return event;
}
},
//把原生事件对象封装为jQ事件对象,并修正不兼容属性
fix: function( event ) {
//经过jQ处理的对象,JQ会为其绑定expando属性,
//如果event已经是jQ的了,就不必做处理了,直接返回即可
if ( event[ jQuery.expando ] ) {
return event;
}
var i, prop,
originalEvent = event,
//获取对应的修正对象
//所有类型的事件对应的fixHooks修正对象只有两种:
//jQuery.event.keyHooks || jQuery.event.mouseHooks
fixHook = jQuery.event.fixHooks[ event.type ] || {},
//copy最终都是事件应该存在的属性,共有的加上两大分类各自私有的
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
//生成一个jQ的对象,这里大家先看jQuery.Event的执行逻辑,然后再回到这里
event = jQuery.Event( originalEvent );
//将原生事件对象上的属性都复制到jQEvent对象上
for ( i = copy.length; i; ) {
prop = copy[ --i ];
event[ prop ] = originalEvent[ prop ];
}
//ie9下,事件源是srcElement属性
if ( !event.target ) {
event.target = originalEvent.srcElement || document;
}
//如果事件源指向的是文本节点,则修正为其父元素节点
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
// metaKey 指的是键盘上的特殊键,ie9下不支持,这里赋值为了ctrlKey
if ( event.metaKey === undefined ) {
event.metaKey = event.ctrlKey;
}
//调用修正对象的filter方法
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
},
//...
}
//src 原生事件类型 || 自定义事件类型 || 原生事件对象 || jQ事件对象
//props 其中的属性将被设置到新创建的jQ事件对象
jQuery.Event = function( src, props ) {
//这里的写法是为了兼容两种形式来进行调用函数
//new fn(); fn();
if ( !(this instanceof jQuery.Event) ) {
return new jQuery.Event( src, props );
}
// Event object
if ( src && src.type ) {
//原始 事件对象,这里是进行了备份
this.originalEvent = src;
//事件类型
this.type = src.type;
//返回 是否event对象被设置了阻止默认事件
this.isDefaultPrevented = (
src.defaultPrevented ||
src.returnValue === false ||
src.getPreventDefault &&
src.getPreventDefault()
) ? returnTrue : returnFalse;
// Event type
} else {
this.type = src;
}
// 将传入进来的对象的属性复制给jQ事件对象
if ( props ) {
jQuery.extend( this, props );
}
// src是事件类型,则把 当前时间 给timeStamp变量
// src是原生事件对象,则把 属性timeStamp 给timeStamp变量
// DOM2的事件模型中,timeStamp属性是一个date对象,
// DOM3的事件模型中,timeStamp属性是数值
this.timeStamp = src && src.timeStamp || jQuery.now();
// 设置jQ的标示符
// jQuery.expando : '1712312312123'
this[ jQuery.expando ] = true;
};
function returnFalse(){
return false;
}
function returnTrue(){
return true;
}
//事件原型,针对阻止默认事件,事件传播封装了兼容性调用方法
jQuery.Event.prototype = {
//阻止浏览器默认事件
preventDefault: function() {
//设置属性为true
this.isDefaultPrevented = returnTrue;
var e = this.originalEvent;
if ( !e )
return;
// 标准浏览器的阻止默认事件
if ( e.preventDefault ) {
e.preventDefault();
} else {
e.returnValue = false;
}
},
//阻止事件冒泡
stopPropagation: function() {
this.isPropagationStopped = returnTrue;
var e = this.originalEvent;
if ( !e ) {
return;
}
// 标准浏览器
if ( e.stopPropagation ) {
e.stopPropagation();
}
// ie
e.cancelBubble = true;
},
//立刻停止事件执行和事件传播
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
},
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse
};
本篇到此就结束了 , 不知道小编本篇的讲解是否到位..
感谢您的阅读 , 欢迎留言: 斧正, 交流, 提问