@@ -29,8 +29,19 @@ function returnFalse() {
2929 return false ;
3030}
3131
32+ // Support: IE <=9 - 11+
33+ // focus() and blur() are asynchronous, except when they are no-op.
34+ // So expect focus to be synchronous when the element is already active,
35+ // and blur to be synchronous when the element is not already active.
36+ // (focus and blur are always synchronous in other supported browsers,
37+ // this just defines when we can count on it).
38+ function expectSync ( elem , type ) {
39+ return ( elem === safeActiveElement ( ) ) === ( type === "focus" ) ;
40+ }
41+
3242// Support: IE <=9 only
33- // See #13393 for more info
43+ // Accessing document.activeElement can throw unexpectedly
44+ // https://bugs.jquery.com/ticket/13393
3445function safeActiveElement ( ) {
3546 try {
3647 return document . activeElement ;
@@ -457,56 +468,6 @@ jQuery.event = {
457468 // Prevent triggered image.load events from bubbling to window.load
458469 noBubble : true
459470 } ,
460- focus : {
461-
462- // Utilize native event if possible so blur/focus sequence is correct
463- setup : function ( ) {
464-
465- // Claim the first handler
466- // dataPriv.set( this, "focus", ... )
467- leverageNative ( this , "focus" , false , function ( el ) {
468- return el !== safeActiveElement ( ) ;
469- } ) ;
470-
471- // Return false to allow normal processing in the caller
472- return false ;
473- } ,
474- trigger : function ( ) {
475-
476- // Force setup before trigger
477- leverageNative ( this , "focus" , returnTrue ) ;
478-
479- // Return non-false to allow normal event-path propagation
480- return true ;
481- } ,
482-
483- delegateType : "focusin"
484- } ,
485- blur : {
486-
487- // Utilize native event if possible so blur/focus sequence is correct
488- setup : function ( ) {
489-
490- // Claim the first handler
491- // dataPriv.set( this, "blur", ... )
492- leverageNative ( this , "blur" , false , function ( el ) {
493- return el === safeActiveElement ( ) ;
494- } ) ;
495-
496- // Return false to allow normal processing in the caller
497- return false ;
498- } ,
499- trigger : function ( ) {
500-
501- // Force setup before trigger
502- leverageNative ( this , "blur" , returnTrue ) ;
503-
504- // Return non-false to allow normal event-path propagation
505- return true ;
506- } ,
507-
508- delegateType : "focusout"
509- } ,
510471 click : {
511472
512473 // Utilize native event to ensure correct state for checkable inputs
@@ -522,7 +483,7 @@ jQuery.event = {
522483 dataPriv . get ( el , "click" ) === undefined ) {
523484
524485 // dataPriv.set( el, "click", ... )
525- leverageNative ( el , "click" , false , returnFalse ) ;
486+ leverageNative ( el , "click" , returnTrue ) ;
526487 }
527488
528489 // Return false to allow normal processing in the caller
@@ -539,7 +500,7 @@ jQuery.event = {
539500 el . click && nodeName ( el , "input" ) &&
540501 dataPriv . get ( el , "click" ) === undefined ) {
541502
542- leverageNative ( el , "click" , returnTrue ) ;
503+ leverageNative ( el , "click" ) ;
543504 }
544505
545506 // Return non-false to allow normal event-path propagation
@@ -574,55 +535,63 @@ jQuery.event = {
574535// synthetic events by interrupting progress until reinvoked in response to
575536// *native* events that it fires directly, ensuring that state changes have
576537// already occurred before other listeners are invoked.
577- function leverageNative ( el , type , forceAdd , allowAsync ) {
538+ function leverageNative ( el , type , expectSync ) {
578539
579- // Setup must go through jQuery.event.add
580- if ( forceAdd ) {
581- jQuery . event . add ( el , type , forceAdd ) ;
540+ // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
541+ if ( ! expectSync ) {
542+ jQuery . event . add ( el , type , returnTrue ) ;
582543 return ;
583544 }
584545
585546 // Register the controller as a special universal handler for all event namespaces
586- dataPriv . set ( el , type , forceAdd ) ;
547+ dataPriv . set ( el , type , false ) ;
587548 jQuery . event . add ( el , type , {
588549 namespace : false ,
589550 handler : function ( event ) {
590- var maybeAsync , result ,
551+ var notAsync , result ,
591552 saved = dataPriv . get ( this , type ) ;
592553
593- // Interrupt processing of the outer synthetic .trigger()ed event
594- if ( ( event . isTrigger & 1 ) && this [ type ] && ! saved ) {
595-
596- // Store arguments for use when handling the inner native event
597- saved = slice . call ( arguments ) ;
598- dataPriv . set ( this , type , saved ) ;
599-
600- // Trigger the native event and capture its result
601- // Support: IE <=9 - 11+
602- // focus() and blur() are asynchronous
603- maybeAsync = allowAsync ( this , type ) ;
604- this [ type ] ( ) ;
605- result = dataPriv . get ( this , type ) ;
606- if ( result !== saved ) {
607- dataPriv . set ( this , type , false ) ;
608-
609- // Cancel the outer synthetic event
610- event . stopImmediatePropagation ( ) ;
611- event . preventDefault ( ) ;
612- return result ;
613- } else if ( maybeAsync ) {
614-
615- // Cancel the outer synthetic event in expectation of a followup
616- event . stopImmediatePropagation ( ) ;
617- event . preventDefault ( ) ;
618- return ;
619- } else {
620- dataPriv . set ( this , type , false ) ;
554+ if ( ( event . isTrigger & 1 ) && this [ type ] ) {
555+
556+ // Interrupt processing of the outer synthetic .trigger()ed event
557+ if ( ! saved ) {
558+
559+ // Store arguments for use when handling the inner native event
560+ saved = slice . call ( arguments ) ;
561+ dataPriv . set ( this , type , saved ) ;
562+
563+ // Trigger the native event and capture its result
564+ // Support: IE <=9 - 11+
565+ // focus() and blur() are asynchronous
566+ notAsync = expectSync ( this , type ) ;
567+ this [ type ] ( ) ;
568+ result = dataPriv . get ( this , type ) ;
569+ if ( saved !== result || notAsync ) {
570+ dataPriv . set ( this , type , false ) ;
571+ } else {
572+ result = undefined ;
573+ }
574+ if ( saved !== result ) {
575+
576+ // Cancel the outer synthetic event
577+ event . stopImmediatePropagation ( ) ;
578+ event . preventDefault ( ) ;
579+ return result ;
580+ }
581+
582+ // If this is an inner synthetic event for an event with a bubbling surrogate
583+ // (focus or blur), assume that the surrogate already propagated from triggering the
584+ // native event and prevent that from happening again here.
585+ // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
586+ // bubbling surrogate propagates *after* the non-bubbling base), but that seems
587+ // less bad than duplication.
588+ } else if ( ( jQuery . event . special [ type ] || { } ) . delegateType ) {
589+ event . stopPropagation ( ) ;
621590 }
622591
623592 // If this is a native event triggered above, everything is now in order
624593 // Fire an inner synthetic event with the original arguments
625- } else if ( ! event . isTrigger && saved ) {
594+ } else if ( saved ) {
626595
627596 // ...and capture the result
628597 dataPriv . set ( this , type , jQuery . event . trigger (
@@ -800,6 +769,33 @@ jQuery.each( {
800769 }
801770} , jQuery . event . addProp ) ;
802771
772+ jQuery . each ( { focus : "focusin" , blur : "focusout" } , function ( type , delegateType ) {
773+ jQuery . event . special [ type ] = {
774+
775+ // Utilize native event if possible so blur/focus sequence is correct
776+ setup : function ( ) {
777+
778+ // Claim the first handler
779+ // dataPriv.set( this, "focus", ... )
780+ // dataPriv.set( this, "blur", ... )
781+ leverageNative ( this , type , expectSync ) ;
782+
783+ // Return false to allow normal processing in the caller
784+ return false ;
785+ } ,
786+ trigger : function ( ) {
787+
788+ // Force setup before trigger
789+ leverageNative ( this , type ) ;
790+
791+ // Return non-false to allow normal event-path propagation
792+ return true ;
793+ } ,
794+
795+ delegateType : delegateType
796+ } ;
797+ } ) ;
798+
803799// Create mouseenter/leave events using mouseover/out and event-time checks
804800// so that event delegation works in jQuery.
805801// Do the same for pointerenter/pointerleave and pointerover/pointerout
0 commit comments