Skip to content

Commit e61fccb

Browse files
jbedarddmethvin
authored andcommittedMay 4, 2016
Event: Remove fixHooks, propHooks; switch to ES5 getter with addProp
Fixes jquerygh-3103 Fixes jquerygh-1746 Closes jquerygh-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
1 parent 7f2ebd2 commit e61fccb

File tree

2 files changed

+122
-96
lines changed

2 files changed

+122
-96
lines changed
 

‎src/event.js

+115-81
Original file line numberDiff line numberDiff line change
@@ -398,90 +398,38 @@ jQuery.event = {
398398
return handlerQueue;
399399
},
400400

401-
// Includes some event props shared by KeyEvent and MouseEvent
402-
props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " +
403-
"metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ),
404-
405-
fixHooks: {},
406-
407-
keyHooks: {
408-
props: "char charCode key keyCode".split( " " ),
409-
filter: function( event, original ) {
410-
411-
// Add which for key events
412-
if ( event.which == null ) {
413-
event.which = original.charCode != null ? original.charCode : original.keyCode;
401+
addProp: function( name, hook ) {
402+
Object.defineProperty( jQuery.Event.prototype, name, {
403+
enumerable: true,
404+
configurable: true,
405+
406+
get: jQuery.isFunction( hook ) ?
407+
function() {
408+
if ( this.originalEvent ) {
409+
return hook( this.originalEvent );
410+
}
411+
} :
412+
function() {
413+
if ( this.originalEvent ) {
414+
return this.originalEvent[ name ];
415+
}
416+
},
417+
418+
set: function( value ) {
419+
Object.defineProperty( this, name, {
420+
enumerable: true,
421+
configurable: true,
422+
writable: true,
423+
value: value
424+
} );
414425
}
415-
416-
return event;
417-
}
426+
} );
418427
},
419428

420-
mouseHooks: {
421-
props: ( "button buttons clientX clientY offsetX offsetY pageX pageY " +
422-
"screenX screenY toElement" ).split( " " ),
423-
filter: function( event, original ) {
424-
var eventDoc, doc, body,
425-
button = original.button;
426-
427-
// Calculate pageX/Y if missing and clientX/Y available
428-
if ( event.pageX == null && original.clientX != null ) {
429-
eventDoc = event.target.ownerDocument || document;
430-
doc = eventDoc.documentElement;
431-
body = eventDoc.body;
432-
433-
event.pageX = original.clientX +
434-
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
435-
( doc && doc.clientLeft || body && body.clientLeft || 0 );
436-
event.pageY = original.clientY +
437-
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
438-
( doc && doc.clientTop || body && body.clientTop || 0 );
439-
}
440-
441-
// Add which for click: 1 === left; 2 === middle; 3 === right
442-
// Note: button is not normalized, so don't use it
443-
if ( !event.which && button !== undefined ) {
444-
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
445-
}
446-
447-
return event;
448-
}
449-
},
450-
451-
fix: function( event ) {
452-
if ( event[ jQuery.expando ] ) {
453-
return event;
454-
}
455-
456-
// Create a writable copy of the event object and normalize some properties
457-
var i, prop, copy,
458-
type = event.type,
459-
originalEvent = event,
460-
fixHook = this.fixHooks[ type ];
461-
462-
if ( !fixHook ) {
463-
this.fixHooks[ type ] = fixHook =
464-
rmouseEvent.test( type ) ? this.mouseHooks :
465-
rkeyEvent.test( type ) ? this.keyHooks :
466-
{};
467-
}
468-
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
469-
470-
event = new jQuery.Event( originalEvent );
471-
472-
i = copy.length;
473-
while ( i-- ) {
474-
prop = copy[ i ];
475-
event[ prop ] = originalEvent[ prop ];
476-
}
477-
478-
// Support: Safari <=6 - 7 only
479-
// Target should not be a text node (#504, #13143)
480-
if ( event.target.nodeType === 3 ) {
481-
event.target = event.target.parentNode;
482-
}
483-
484-
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
429+
fix: function( originalEvent ) {
430+
return originalEvent[ jQuery.expando ] ?
431+
originalEvent :
432+
new jQuery.Event( originalEvent );
485433
},
486434

487435
special: {
@@ -569,6 +517,16 @@ jQuery.Event = function( src, props ) {
569517
returnTrue :
570518
returnFalse;
571519

520+
// Create target properties
521+
// Support: Safari <=6 - 7 only
522+
// Target should not be a text node (#504, #13143)
523+
this.target = ( src.target.nodeType === 3 ) ?
524+
src.target.parentNode :
525+
src.target;
526+
527+
this.currentTarget = src.currentTarget;
528+
this.relatedTarget = src.relatedTarget;
529+
572530
// Event type
573531
} else {
574532
this.type = src;
@@ -625,6 +583,82 @@ jQuery.Event.prototype = {
625583
}
626584
};
627585

586+
// Includes all common event props including KeyEvent and MouseEvent specific props
587+
jQuery.each( {
588+
altKey: true,
589+
bubbles: true,
590+
cancelable: true,
591+
ctrlKey: true,
592+
detail: true,
593+
eventPhase: true,
594+
metaKey: true,
595+
shiftKey: true,
596+
view: true,
597+
"char": true,
598+
charCode: true,
599+
key: true,
600+
keyCode: true,
601+
button: true,
602+
buttons: true,
603+
clientX: true,
604+
clientY: true,
605+
offsetX: true,
606+
offsetY: true,
607+
screenX: true,
608+
screenY: true,
609+
toElement: true,
610+
611+
which: function( event ) {
612+
var button = event.button;
613+
614+
// Add which for key events
615+
if ( event.which == null && rkeyEvent.test( event.type ) ) {
616+
return event.charCode != null ? event.charCode : event.keyCode;
617+
}
618+
619+
// Add which for click: 1 === left; 2 === middle; 3 === right
620+
if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
621+
return ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
622+
}
623+
624+
return event.which;
625+
},
626+
627+
pageX: function( event ) {
628+
var eventDoc, doc, body;
629+
630+
// Calculate pageX if missing and clientX available
631+
if ( event.pageX == null && event.clientX != null ) {
632+
eventDoc = event.target.ownerDocument || document;
633+
doc = eventDoc.documentElement;
634+
body = eventDoc.body;
635+
636+
return event.clientX +
637+
( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
638+
( doc && doc.clientLeft || body && body.clientLeft || 0 );
639+
}
640+
641+
return event.pageX;
642+
},
643+
644+
pageY: function( event ) {
645+
var eventDoc, doc, body;
646+
647+
// Calculate pageY if missing and clientY available
648+
if ( event.pageY == null && event.clientY != null ) {
649+
eventDoc = event.target.ownerDocument || document;
650+
doc = eventDoc.documentElement;
651+
body = eventDoc.body;
652+
653+
return event.clientY +
654+
( doc && doc.scrollTop || body && body.scrollTop || 0 ) -
655+
( doc && doc.clientTop || body && body.clientTop || 0 );
656+
}
657+
658+
return event.pageY;
659+
}
660+
}, jQuery.event.addProp );
661+
628662
// Create mouseenter/leave events using mouseover/out and event-time checks
629663
// so that event delegation works in jQuery.
630664
// Do the same for pointerenter/pointerleave and pointerover/pointerout

‎test/unit/event.js

+7-15
Original file line numberDiff line numberDiff line change
@@ -2410,36 +2410,28 @@ QUnit.test( "event object properties on natively-triggered event", function( ass
24102410
$link.off( "click" ).remove();
24112411
} );
24122412

2413-
QUnit.test( "fixHooks extensions", function( assert ) {
2413+
QUnit.test( "addProp extensions", function( assert ) {
24142414
assert.expect( 2 );
24152415

2416-
// IE requires focusable elements to be visible, so append to body
2417-
var $fixture = jQuery( "<input type='text' id='hook-fixture' />" ).appendTo( "body" ),
2418-
saved = jQuery.event.fixHooks.click;
2416+
var $fixture = jQuery( "<div>" ).appendTo( "#qunit-fixture" );
24192417

24202418
// Ensure the property doesn't exist
24212419
$fixture.on( "click", function( event ) {
2422-
assert.ok( !( "blurrinessLevel" in event ), "event.blurrinessLevel does not exist" );
2420+
assert.ok( !( "testProperty" in event ), "event.testProperty does not exist" );
24232421
} );
24242422
fireNative( $fixture[ 0 ], "click" );
24252423
$fixture.off( "click" );
24262424

2427-
jQuery.event.fixHooks.click = {
2428-
filter: function( event ) {
2429-
event.blurrinessLevel = 42;
2430-
return event;
2431-
}
2432-
};
2425+
jQuery.event.addProp( "testProperty", function() { return 42; } );
24332426

24342427
// Trigger a native click and ensure the property is set
24352428
$fixture.on( "click", function( event ) {
2436-
assert.equal( event.blurrinessLevel, 42, "event.blurrinessLevel was set" );
2429+
assert.equal( event.testProperty, 42, "event.testProperty getter was invoked" );
24372430
} );
24382431
fireNative( $fixture[ 0 ], "click" );
2432+
$fixture.off( "click" );
24392433

2440-
delete jQuery.event.fixHooks.click;
2441-
$fixture.off( "click" ).remove();
2442-
jQuery.event.fixHooks.click = saved;
2434+
$fixture.remove();
24432435
} );
24442436

24452437
QUnit.test( "drag/drop events copy mouse-related event properties (gh-1925, gh-2009)", function( assert ) {

0 commit comments

Comments
 (0)