@@ -27,25 +27,6 @@ function returnFalse() {
2727 return false ;
2828}
2929
30- // Support: IE <=9 - 11+
31- // focus() and blur() are asynchronous, except when they are no-op.
32- // So expect focus to be synchronous when the element is already active,
33- // and blur to be synchronous when the element is not already active.
34- // (focus and blur are always synchronous in other supported browsers,
35- // this just defines when we can count on it).
36- function expectSync ( elem , type ) {
37- return ( elem === safeActiveElement ( ) ) === ( type === "focus" ) ;
38- }
39-
40- // Support: IE <=9 only
41- // Accessing document.activeElement can throw unexpectedly
42- // https://bugs.jquery.com/ticket/13393
43- function safeActiveElement ( ) {
44- try {
45- return document . activeElement ;
46- } catch ( err ) { }
47- }
48-
4930function on ( elem , types , selector , data , fn , one ) {
5031 var origFn , type ;
5132
@@ -483,7 +464,7 @@ jQuery.event = {
483464 el . click && nodeName ( el , "input" ) ) {
484465
485466 // dataPriv.set( el, "click", ... )
486- leverageNative ( el , "click" , returnTrue ) ;
467+ leverageNative ( el , "click" , true ) ;
487468 }
488469
489470 // Return false to allow normal processing in the caller
@@ -534,10 +515,10 @@ jQuery.event = {
534515// synthetic events by interrupting progress until reinvoked in response to
535516// *native* events that it fires directly, ensuring that state changes have
536517// already occurred before other listeners are invoked.
537- function leverageNative ( el , type , expectSync ) {
518+ function leverageNative ( el , type , isSetup ) {
538519
539- // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
540- if ( ! expectSync ) {
520+ // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add
521+ if ( ! isSetup ) {
541522 if ( dataPriv . get ( el , type ) === undefined ) {
542523 jQuery . event . add ( el , type , returnTrue ) ;
543524 }
@@ -549,15 +530,13 @@ function leverageNative( el, type, expectSync ) {
549530 jQuery . event . add ( el , type , {
550531 namespace : false ,
551532 handler : function ( event ) {
552- var notAsync , result ,
533+ var result ,
553534 saved = dataPriv . get ( this , type ) ;
554535
555536 if ( ( event . isTrigger & 1 ) && this [ type ] ) {
556537
557538 // Interrupt processing of the outer synthetic .trigger()ed event
558- // Saved data should be false in such cases, but might be a leftover capture object
559- // from an async native handler (gh-4350)
560- if ( ! saved . length ) {
539+ if ( ! saved ) {
561540
562541 // Store arguments for use when handling the inner native event
563542 // There will always be at least one argument (an event object), so this array
@@ -566,28 +545,17 @@ function leverageNative( el, type, expectSync ) {
566545 dataPriv . set ( this , type , saved ) ;
567546
568547 // Trigger the native event and capture its result
569- // Support: IE <=9 - 11+
570- // focus() and blur() are asynchronous
571- notAsync = expectSync ( this , type ) ;
572548 this [ type ] ( ) ;
573549 result = dataPriv . get ( this , type ) ;
574- if ( saved !== result || notAsync ) {
575- dataPriv . set ( this , type , false ) ;
576- } else {
577- result = { } ;
578- }
550+ dataPriv . set ( this , type , false ) ;
551+
579552 if ( saved !== result ) {
580553
581554 // Cancel the outer synthetic event
582555 event . stopImmediatePropagation ( ) ;
583556 event . preventDefault ( ) ;
584557
585- // Support: Chrome 86+
586- // In Chrome, if an element having a focusout handler is blurred by
587- // clicking outside of it, it invokes the handler synchronously. If
588- // that handler calls `.remove()` on the element, the data is cleared,
589- // leaving `result` undefined. We need to guard against this.
590- return result && result . value ;
558+ return result ;
591559 }
592560
593561 // If this is an inner synthetic event for an event with a bubbling surrogate
@@ -605,16 +573,11 @@ function leverageNative( el, type, expectSync ) {
605573 } else if ( saved . length ) {
606574
607575 // ...and capture the result
608- dataPriv . set ( this , type , {
609- value : jQuery . event . trigger (
610-
611- // Support: IE <=9 - 11+
612- // Extend with the prototype to reset the above stopImmediatePropagation()
613- jQuery . extend ( saved [ 0 ] , jQuery . Event . prototype ) ,
614- saved . slice ( 1 ) ,
615- this
616- )
617- } ) ;
576+ dataPriv . set ( this , type , jQuery . event . trigger (
577+ saved [ 0 ] ,
578+ saved . slice ( 1 ) ,
579+ this
580+ ) ) ;
618581
619582 // Abort handling of the native event
620583 event . stopImmediatePropagation ( ) ;
@@ -756,18 +719,73 @@ jQuery.each( {
756719} , jQuery . event . addProp ) ;
757720
758721jQuery . each ( { focus : "focusin" , blur : "focusout" } , function ( type , delegateType ) {
722+
723+ function focusMappedHandler ( nativeEvent ) {
724+ if ( document . documentMode ) {
725+
726+ // Support: IE 11+
727+ // Attach a single focusin/focusout handler on the document while someone wants
728+ // focus/blur. This is because the former are synchronous in IE while the latter
729+ // are async. In other browsers, all those handlers are invoked synchronously.
730+
731+ // `handle` from private data would already wrap the event, but we need
732+ // to change the `type` here.
733+ var handle = dataPriv . get ( this , "handle" ) ,
734+ event = jQuery . event . fix ( nativeEvent ) ;
735+ event . type = nativeEvent . type === "focusin" ? "focus" : "blur" ;
736+ event . isSimulated = true ;
737+
738+ // First, handle focusin/focusout
739+ handle ( nativeEvent ) ;
740+
741+ // ...then, handle focus/blur
742+ //
743+ // focus/blur don't bubble while focusin/focusout do; simulate the former by only
744+ // invoking the handler at the lower level.
745+ if ( event . target === event . currentTarget ) {
746+
747+ // The setup part calls `leverageNative`, which, in turn, calls
748+ // `jQuery.event.add`, so event handle will already have been set
749+ // by this point.
750+ handle ( event ) ;
751+ }
752+ } else {
753+
754+ // For non-IE browsers, attach a single capturing handler on the document
755+ // while someone wants focusin/focusout.
756+ jQuery . event . simulate ( delegateType , nativeEvent . target ,
757+ jQuery . event . fix ( nativeEvent ) ) ;
758+ }
759+ }
760+
759761 jQuery . event . special [ type ] = {
760762
761763 // Utilize native event if possible so blur/focus sequence is correct
762764 setup : function ( ) {
763765
766+ var attaches ;
767+
764768 // Claim the first handler
765769 // dataPriv.set( this, "focus", ... )
766770 // dataPriv.set( this, "blur", ... )
767- leverageNative ( this , type , expectSync ) ;
771+ leverageNative ( this , type , true ) ;
772+
773+ if ( document . documentMode ) {
768774
769- // Return false to allow normal processing in the caller
770- return false ;
775+ // Support: IE 9 - 11+
776+ // We use the same native handler for focusin & focus (and focusout & blur)
777+ // so we need to coordinate setup & teardown parts between those events.
778+ // Use `delegateType` as the key as `type` is already used by `leverageNative`.
779+ attaches = dataPriv . get ( this , delegateType ) ;
780+ if ( ! attaches ) {
781+ this . addEventListener ( delegateType , focusMappedHandler ) ;
782+ }
783+ dataPriv . set ( this , delegateType , ( attaches || 0 ) + 1 ) ;
784+ } else {
785+
786+ // Return false to allow normal processing in the caller
787+ return false ;
788+ }
771789 } ,
772790 trigger : function ( ) {
773791
@@ -778,6 +796,24 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
778796 return true ;
779797 } ,
780798
799+ teardown : function ( ) {
800+ var attaches ;
801+
802+ if ( document . documentMode ) {
803+ attaches = dataPriv . get ( this , delegateType ) - 1 ;
804+ if ( ! attaches ) {
805+ this . removeEventListener ( delegateType , focusMappedHandler ) ;
806+ dataPriv . remove ( this , delegateType ) ;
807+ } else {
808+ dataPriv . set ( this , delegateType , attaches ) ;
809+ }
810+ } else {
811+
812+ // Return false to indicate standard teardown should be applied
813+ return false ;
814+ }
815+ } ,
816+
781817 // Suppress native focus or blur if we're currently inside
782818 // a leveraged native-event stack
783819 _default : function ( event ) {
@@ -786,6 +822,58 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
786822
787823 delegateType : delegateType
788824 } ;
825+
826+ // Support: Firefox <=44
827+ // Firefox doesn't have focus(in | out) events
828+ // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
829+ //
830+ // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
831+ // focus(in | out) events fire after focus & blur events,
832+ // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
833+ // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
834+ //
835+ // Support: IE 9 - 11+
836+ // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch,
837+ // attach a single handler for both events in IE.
838+ jQuery . event . special [ delegateType ] = {
839+ setup : function ( ) {
840+
841+ // Handle: regular nodes (via `this.ownerDocument`), window
842+ // (via `this.document`) & document (via `this`).
843+ var doc = this . ownerDocument || this . document || this ,
844+ dataHolder = document . documentMode ? this : doc ,
845+ attaches = dataPriv . get ( dataHolder , delegateType ) ;
846+
847+ // Support: IE 9 - 11+
848+ // We use the same native handler for focusin & focus (and focusout & blur)
849+ // so we need to coordinate setup & teardown parts between those events.
850+ // Use `delegateType` as the key as `type` is already used by `leverageNative`.
851+ if ( ! attaches ) {
852+ if ( document . documentMode ) {
853+ this . addEventListener ( delegateType , focusMappedHandler ) ;
854+ } else {
855+ doc . addEventListener ( type , focusMappedHandler , true ) ;
856+ }
857+ }
858+ dataPriv . set ( dataHolder , delegateType , ( attaches || 0 ) + 1 ) ;
859+ } ,
860+ teardown : function ( ) {
861+ var doc = this . ownerDocument || this . document || this ,
862+ dataHolder = document . documentMode ? this : doc ,
863+ attaches = dataPriv . get ( dataHolder , delegateType ) - 1 ;
864+
865+ if ( ! attaches ) {
866+ if ( document . documentMode ) {
867+ this . removeEventListener ( delegateType , focusMappedHandler ) ;
868+ } else {
869+ doc . removeEventListener ( type , focusMappedHandler , true ) ;
870+ }
871+ dataPriv . remove ( dataHolder , delegateType ) ;
872+ } else {
873+ dataPriv . set ( dataHolder , delegateType , attaches ) ;
874+ }
875+ }
876+ } ;
789877} ) ;
790878
791879// Create mouseenter/leave events using mouseover/out and event-time checks
0 commit comments