mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Event: Remove fixHooks, propHooks; switch to ES5 getter with addProp
Fixes gh-3103 Fixes gh-1746 Closes gh-2860 - Removes the copy loop in jQuery.event.fix - Avoids accessing properties such as client/offset/page/screen X/Y which may cause style recalc or layouts - Simplifies adding property hooks to event object
This commit is contained in:
parent
7f2ebd2c4d
commit
e61fccb9d7
192
src/event.js
192
src/event.js
@ -398,90 +398,38 @@ jQuery.event = {
|
|||||||
return handlerQueue;
|
return handlerQueue;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Includes some event props shared by KeyEvent and MouseEvent
|
addProp: function( name, hook ) {
|
||||||
props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
|
Object.defineProperty( jQuery.Event.prototype, name, {
|
||||||
"metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
|
||||||
fixHooks: {},
|
get: jQuery.isFunction( hook ) ?
|
||||||
|
function() {
|
||||||
|
if ( this.originalEvent ) {
|
||||||
|
return hook( this.originalEvent );
|
||||||
|
}
|
||||||
|
} :
|
||||||
|
function() {
|
||||||
|
if ( this.originalEvent ) {
|
||||||
|
return this.originalEvent[ name ];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
keyHooks: {
|
set: function( value ) {
|
||||||
props: "char charCode key keyCode".split( " " ),
|
Object.defineProperty( this, name, {
|
||||||
filter: function( event, original ) {
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
// Add which for key events
|
writable: true,
|
||||||
if ( event.which == null ) {
|
value: value
|
||||||
event.which = original.charCode != null ? original.charCode : original.keyCode;
|
} );
|
||||||
}
|
}
|
||||||
|
} );
|
||||||
return event;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mouseHooks: {
|
fix: function( originalEvent ) {
|
||||||
props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " +
|
return originalEvent[ jQuery.expando ] ?
|
||||||
"screenX screenY toElement" ).split( " " ),
|
originalEvent :
|
||||||
filter: function( event, original ) {
|
new jQuery.Event( originalEvent );
|
||||||
var eventDoc, doc, body,
|
|
||||||
button = original.button;
|
|
||||||
|
|
||||||
// Calculate pageX/Y if missing and clientX/Y available
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add which for click: 1 === left; 2 === middle; 3 === right
|
|
||||||
// Note: button is not normalized, so don't use it
|
|
||||||
if ( !event.which && button !== undefined ) {
|
|
||||||
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
fix: function( event ) {
|
|
||||||
if ( event[ jQuery.expando ] ) {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a writable copy of the event object and normalize some properties
|
|
||||||
var i, prop, copy,
|
|
||||||
type = event.type,
|
|
||||||
originalEvent = event,
|
|
||||||
fixHook = this.fixHooks[ type ];
|
|
||||||
|
|
||||||
if ( !fixHook ) {
|
|
||||||
this.fixHooks[ type ] = fixHook =
|
|
||||||
rmouseEvent.test( type ) ? this.mouseHooks :
|
|
||||||
rkeyEvent.test( type ) ? this.keyHooks :
|
|
||||||
{};
|
|
||||||
}
|
|
||||||
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
|
|
||||||
|
|
||||||
event = new jQuery.Event( originalEvent );
|
|
||||||
|
|
||||||
i = copy.length;
|
|
||||||
while ( i-- ) {
|
|
||||||
prop = copy[ i ];
|
|
||||||
event[ prop ] = originalEvent[ prop ];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support: Safari <=6 - 7 only
|
|
||||||
// Target should not be a text node (#504, #13143)
|
|
||||||
if ( event.target.nodeType === 3 ) {
|
|
||||||
event.target = event.target.parentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
special: {
|
special: {
|
||||||
@ -569,6 +517,16 @@ jQuery.Event = function( src, props ) {
|
|||||||
returnTrue :
|
returnTrue :
|
||||||
returnFalse;
|
returnFalse;
|
||||||
|
|
||||||
|
// Create target properties
|
||||||
|
// Support: Safari <=6 - 7 only
|
||||||
|
// Target should not be a text node (#504, #13143)
|
||||||
|
this.target = ( src.target.nodeType === 3 ) ?
|
||||||
|
src.target.parentNode :
|
||||||
|
src.target;
|
||||||
|
|
||||||
|
this.currentTarget = src.currentTarget;
|
||||||
|
this.relatedTarget = src.relatedTarget;
|
||||||
|
|
||||||
// Event type
|
// Event type
|
||||||
} else {
|
} else {
|
||||||
this.type = src;
|
this.type = src;
|
||||||
@ -625,6 +583,82 @@ jQuery.Event.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Includes all common event props including KeyEvent and MouseEvent specific props
|
||||||
|
jQuery.each( {
|
||||||
|
altKey: true,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
ctrlKey: true,
|
||||||
|
detail: true,
|
||||||
|
eventPhase: true,
|
||||||
|
metaKey: true,
|
||||||
|
shiftKey: true,
|
||||||
|
view: true,
|
||||||
|
"char": true,
|
||||||
|
charCode: true,
|
||||||
|
key: true,
|
||||||
|
keyCode: true,
|
||||||
|
button: true,
|
||||||
|
buttons: true,
|
||||||
|
clientX: true,
|
||||||
|
clientY: true,
|
||||||
|
offsetX: true,
|
||||||
|
offsetY: true,
|
||||||
|
screenX: true,
|
||||||
|
screenY: true,
|
||||||
|
toElement: true,
|
||||||
|
|
||||||
|
which: function( event ) {
|
||||||
|
var button = event.button;
|
||||||
|
|
||||||
|
// Add which for key events
|
||||||
|
if ( event.which == null && rkeyEvent.test( event.type ) ) {
|
||||||
|
return event.charCode != null ? event.charCode : event.keyCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add which for click: 1 === left; 2 === middle; 3 === right
|
||||||
|
if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
|
||||||
|
return ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.which;
|
||||||
|
},
|
||||||
|
|
||||||
|
pageX: function( event ) {
|
||||||
|
var eventDoc, doc, body;
|
||||||
|
|
||||||
|
// Calculate pageX if missing and clientX available
|
||||||
|
if ( event.pageX == null && event.clientX != null ) {
|
||||||
|
eventDoc = event.target.ownerDocument || document;
|
||||||
|
doc = eventDoc.documentElement;
|
||||||
|
body = eventDoc.body;
|
||||||
|
|
||||||
|
return event.clientX +
|
||||||
|
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
|
||||||
|
( doc && doc.clientLeft || body && body.clientLeft || 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.pageX;
|
||||||
|
},
|
||||||
|
|
||||||
|
pageY: function( event ) {
|
||||||
|
var eventDoc, doc, body;
|
||||||
|
|
||||||
|
// Calculate pageY if missing and clientY available
|
||||||
|
if ( event.pageY == null && event.clientY != null ) {
|
||||||
|
eventDoc = event.target.ownerDocument || document;
|
||||||
|
doc = eventDoc.documentElement;
|
||||||
|
body = eventDoc.body;
|
||||||
|
|
||||||
|
return event.clientY +
|
||||||
|
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
|
||||||
|
( doc && doc.clientTop || body && body.clientTop || 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return event.pageY;
|
||||||
|
}
|
||||||
|
}, jQuery.event.addProp );
|
||||||
|
|
||||||
// Create mouseenter/leave events using mouseover/out and event-time checks
|
// Create mouseenter/leave events using mouseover/out and event-time checks
|
||||||
// so that event delegation works in jQuery.
|
// so that event delegation works in jQuery.
|
||||||
// Do the same for pointerenter/pointerleave and pointerover/pointerout
|
// Do the same for pointerenter/pointerleave and pointerover/pointerout
|
||||||
|
@ -2410,36 +2410,28 @@ QUnit.test( "event object properties on natively-triggered event", function( ass
|
|||||||
$link.off( "click" ).remove();
|
$link.off( "click" ).remove();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "fixHooks extensions", function( assert ) {
|
QUnit.test( "addProp extensions", function( assert ) {
|
||||||
assert.expect( 2 );
|
assert.expect( 2 );
|
||||||
|
|
||||||
// IE requires focusable elements to be visible, so append to body
|
var $fixture = jQuery( "<div>" ).appendTo( "#qunit-fixture" );
|
||||||
var $fixture = jQuery( "<input type='text' id='hook-fixture' />" ).appendTo( "body" ),
|
|
||||||
saved = jQuery.event.fixHooks.click;
|
|
||||||
|
|
||||||
// Ensure the property doesn't exist
|
// Ensure the property doesn't exist
|
||||||
$fixture.on( "click", function( event ) {
|
$fixture.on( "click", function( event ) {
|
||||||
assert.ok( !( "blurrinessLevel" in event ), "event.blurrinessLevel does not exist" );
|
assert.ok( !( "testProperty" in event ), "event.testProperty does not exist" );
|
||||||
} );
|
} );
|
||||||
fireNative( $fixture[ 0 ], "click" );
|
fireNative( $fixture[ 0 ], "click" );
|
||||||
$fixture.off( "click" );
|
$fixture.off( "click" );
|
||||||
|
|
||||||
jQuery.event.fixHooks.click = {
|
jQuery.event.addProp( "testProperty", function() { return 42; } );
|
||||||
filter: function( event ) {
|
|
||||||
event.blurrinessLevel = 42;
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Trigger a native click and ensure the property is set
|
// Trigger a native click and ensure the property is set
|
||||||
$fixture.on( "click", function( event ) {
|
$fixture.on( "click", function( event ) {
|
||||||
assert.equal( event.blurrinessLevel, 42, "event.blurrinessLevel was set" );
|
assert.equal( event.testProperty, 42, "event.testProperty getter was invoked" );
|
||||||
} );
|
} );
|
||||||
fireNative( $fixture[ 0 ], "click" );
|
fireNative( $fixture[ 0 ], "click" );
|
||||||
|
$fixture.off( "click" );
|
||||||
|
|
||||||
delete jQuery.event.fixHooks.click;
|
$fixture.remove();
|
||||||
$fixture.off( "click" ).remove();
|
|
||||||
jQuery.event.fixHooks.click = saved;
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "drag/drop events copy mouse-related event properties (gh-1925, gh-2009)", function( assert ) {
|
QUnit.test( "drag/drop events copy mouse-related event properties (gh-1925, gh-2009)", function( assert ) {
|
||||||
|
Loading…
Reference in New Issue
Block a user