In the previous article, I introduced you to the jQuery-1.9.1 source code analysis series (10) event system event architecture. This article continues to introduce to you the relevant knowledge of the jquery1.9.1 source code analysis series. Please see below for details.
First of all, you need to understand that the browser's native events are read-only, which limits jQuery's operations on them. A simple example will help you understand why jQuery must construct a new event object.
In delegation processing, node a delegates node b to execute the fn function when a is clicked. When the event bubbles to node b, the context environment needs to be correct when executing fn. It is node a that executes fn, not node b. How to ensure that the context of executing fn is node a: look at the source code (red part)
//执行 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ).apply( matched.elem, args );
Use apply to replace the context of the execution function with a node (matched.elem). Another point is that args[0] is the event object event. How to ensure that the event is an event of node a? This is the function of the important attribute event.currentTarget, so one step is done before executing apply
event.currentTarget = matched.elem;
Directly change the currentTarget property of the event object, which is not possible in browser local events. That's why we have jQuery's event object constructed based on local events.
There are two types of events: mouse events and keyboard events (I don’t know when touch events can be added). Take a look at the detailed properties of these two
Some of them are browser-specific and not W3C standards. jQuery divides event properties into three parts
Properties shared by mouse and keyboard events jQuery.event.props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" ")
Keyboard event-specific properties jQuery.event.keyHooks.props: "char charCode key keyCode".split(" ")
Mouse event-specific properties jQuery.event.mouseHooks.props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" ")
a. Construct a new event object jQuery.event.fix(originalEvent)
Constructing a new event object is completed in three steps
In the first step, use event = new jQuery.Event(originalEvent) to construct a new event object (if you don’t understand the role of new, please click here), and add isDefaultPrevented, originalEvent, type, and timeStamp when creating the event. And the event has been corrected mark (optimized use to avoid unnecessary processing). The source code of jQuery.Event(src, props) is as follows
jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } //src为事件对象 if ( src && src.type ) { this.originalEvent = src; this.type = src.type; //事件冒泡的文档可能被标记为阻止默认事件发生;这个函数可以反应是否阻止的标志的正确值 this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; //src为事件类型 } else { this.type = src; } //将明确提供的特征添加到事件对象上 if ( props ) { jQuery.extend( this, props ); } //创建一个时间戳如果传入的事件不只一个 this.timeStamp = src && src.timeStamp || jQuery.now(); //标记事件已经修正过 this[ jQuery.expando ] = true; };
The event object constructed in the first step
The second step is to identify what kind of event the current event is, and then copy the corresponding attributes one by one from the browser's local event originalEvent
//创建可写的事件对象副本,并格式化一些特征名称 var i, prop, copy, type = event.type, originalEvent = event, fixHook = this.fixHooks[ type ]; if ( !fixHook ) { this.fixHooks[ type ] = fixHook = //rmouseEvent=/^(?:mouse|contextmenu)|click/ rmouseEvent.test( type ) ? this.mouseHooks : //rkeyEvent=/^key/ rkeyEvent.test( type ) ? this.keyHooks : {}; } //获得要从原生事件中拷贝过来的属性列表 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; ... //将原生的属性都拷贝到新的事件上 i = copy.length; while ( i-- ) { prop = copy[ i ]; event[ prop ] = originalEvent[ prop ]; }
The third step is compatibility processing of related attributes
// IE<9修正target特征值 if ( !event.target ) { event.target = originalEvent.srcElement || document; } // Chrome 23+, Safari?,Target特征值不能是文本节点 if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } // IE<9,对于鼠标/键盘事件, 如果metaKey没有定义则设置metaKey==false event.metaKey = !!event.metaKey; //调用hooks的filter return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
The last line of code performs compatible adaptation processing for mouse events and keyboard events.
fixHook.filter may be jQuery.event.keyHooks.filter
keyHooks.filter: function( event, original ) { //给键盘事件添加which特征值 if ( event.which == null ) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; }
or this jQuery.event.mouseHooks.filter
mouseHooks.filter: function( event, original ) { var body, eventDoc, doc, button = original.button, fromElement = original.fromElement; //如果事件pageX/Y特征不见了,用可用的clientX/Y来计算出来 if ( event.pageX == null && original.clientX != null ) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; 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; } //添加点击事件which特征值: 1 === left; 2 === middle; 3 === right //备注:button不标准,因此不要是使用 if ( !event.which && button !== undefined ) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; }
The latest event object completed is as follows (taking mouse events as an example)
Original events are saved in originalEvent, target saves the target node (delegated node, event source), and other information is skipped
b. Overload event method
When constructing a new event object event = new jQuery.Event(originalEvent), the event will inherit the methods in jQuery.event.prototype. Let’s take a look at the methods
We previously analyzed the role of the overloaded stopPropagation method in jQuery.event.prototype: in addition to calling the event object's method to prevent bubbling, there is also a role when the delegated node has multiple delegated events waiting to be processed. , one of the events calling event.stopPropagation() will prevent the execution of subsequent event processing. Click here to search for keywords to view
The preventDefault function also has a similar effect. This code was added to the preventDefault function
this.isPropagationStopped = returnTrue;
在触发事件trigger函数和模拟冒泡simulate函数中都会根据isPropagationStopped()判断是否要执行DOM节点的默认操作。源码如下
isImmediatePropagationStopped是stopPropagation特殊用法,isImmediatePropagationStopped会直接阻止掉当前的处理和后面等待执行的事件处理,而stopPropagation会执行完当前的处理,然后阻止后面等待执行的事件处理。
源码如下
// jQuery.Event基于DOM事件所指定的ECMAScript语言绑定 // http://www.w.org/TR//WD-DOM-Level--Events-/ecma-script-binding.html jQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( !e ) {return; } if ( e.preventDefault ) { e.preventDefault(); //IE支持 } else { e.returnValue = false; } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( !e ) {return; } if ( e.stopPropagation ) { e.stopPropagation(); } // IE支持 e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); } }
以上就是本文给大家介绍的jQuery-1.9.1源码分析系列(十)事件系统之事件包装,希望大家喜欢。