🌐 AI搜索 & 代理 主页
Skip to content

Commit c052f78

Browse files
committed
Event: Fix focusin/focus & focusout/blur order in IE
To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, attach a single handler for both events in IE. A side effect of this is that to reduce size the `event/focusin` module no longer exists and it's impossible to disable the focusin patch in modern browsers via the jQuery custom build system.
1 parent 47cf853 commit c052f78

File tree

6 files changed

+106
-105
lines changed

6 files changed

+106
-105
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ Some example modules that can be excluded are:
9595
- **dimensions**: The `.width()` and `.height()` methods, including `inner-` and `outer-` variations.
9696
- **effects**: The `.animate()` method and its shorthands such as `.slideUp()` or `.hide("slow")`.
9797
- **event**: The `.on()` and `.off()` methods and all event functionality.
98-
- **event/focusin**: Cross-browser support for the focusin and focusout events.
9998
- **event/trigger**: The `.trigger()` and `.triggerHandler()` methods.
10099
- **offset**: The `.offset()`, `.position()`, `.offsetParent()`, `.scrollLeft()`, and `.scrollTop()` methods.
101100
- **wrap**: The `.wrap()`, `.wrapAll()`, `.wrapInner()`, and `.unwrap()` methods.

src/event.js

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -730,25 +730,41 @@ jQuery.each( {
730730

731731
jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
732732

733-
// Support: IE 11+
734-
// Attach a single focusin/focusout handler on the document while someone wants focus/blur.
735-
// This is because the former are synchronous in IE while the latter are async. In other
736-
// browsers, all those handlers are invoked synchronously.
737733
function focusMappedHandler( nativeEvent ) {
734+
if ( document.documentMode ) {
735+
736+
// Support: IE 11+
737+
// Attach a single focusin/focusout handler on the document while someone wants
738+
// focus/blur. This is because the former are synchronous in IE while the latter
739+
// are async. In other browsers, all those handlers are invoked synchronously.
740+
741+
// `handle` from private data would already wrap the event, but we need
742+
// to change the `type` here.
743+
var handle = dataPriv.get( this, "handle" ),
744+
event = jQuery.event.fix( nativeEvent );
745+
event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
746+
event.isSimulated = true;
747+
748+
// First, handle focusin/focusout
749+
handle( nativeEvent );
750+
751+
// ...then, handle focus/blur
752+
//
753+
// focus/blur don't bubble while focusin/focusout do; simulate the former by only
754+
// invoking the handler at the lower level.
755+
if ( event.target === event.currentTarget ) {
756+
757+
// The setup part calls `leverageNative`, which, in turn, calls
758+
// `jQuery.event.add`, so event handle will already have been set
759+
// by this point.
760+
handle( event );
761+
}
762+
} else {
738763

739-
// `eventHandle` would already wrap the event, but we need to change the `type` here.
740-
var event = jQuery.event.fix( nativeEvent );
741-
event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
742-
event.isSimulated = true;
743-
744-
// focus/blur don't bubble while focusin/focusout do; simulate the former by only
745-
// invoking the handler at the lower level.
746-
if ( event.target === event.currentTarget ) {
747-
748-
// The setup part calls `leverageNative`, which, in turn, calls
749-
// `jQuery.event.add`, so event handle will already have been set
750-
// by this point.
751-
dataPriv.get( this, "handle" )( event );
764+
// For non-IE browsers, attach a single capturing handler on the document
765+
// while someone wants focusin/focusout.
766+
jQuery.event.simulate( delegateType, nativeEvent.target,
767+
jQuery.event.fix( nativeEvent ) );
752768
}
753769
}
754770

@@ -757,13 +773,24 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
757773
// Utilize native event if possible so blur/focus sequence is correct
758774
setup: function() {
759775

776+
var attaches;
777+
760778
// Claim the first handler
761779
// dataPriv.set( this, "focus", ... )
762780
// dataPriv.set( this, "blur", ... )
763781
leverageNative( this, type, true );
764782

765783
if ( document.documentMode ) {
766-
this.addEventListener( delegateType, focusMappedHandler );
784+
785+
// Support: IE 9 - 11+
786+
// We use the same native handler for focusin & focus (and focusout & blur)
787+
// so we need to coordinate setup & teardown parts between those events.
788+
// Use `delegateType` as the key as `type` is already used by `leverageNative`.
789+
attaches = dataPriv.get( this, delegateType );
790+
if ( !attaches ) {
791+
this.addEventListener( delegateType, focusMappedHandler );
792+
}
793+
dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 );
767794
} else {
768795

769796
// Return false to allow normal processing in the caller
@@ -780,8 +807,16 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
780807
},
781808

782809
teardown: function() {
810+
var attaches;
811+
783812
if ( document.documentMode ) {
784-
this.removeEventListener( delegateType, focusMappedHandler );
813+
attaches = dataPriv.get( this, delegateType ) - 1;
814+
if ( !attaches ) {
815+
this.removeEventListener( delegateType, focusMappedHandler );
816+
dataPriv.remove( this, delegateType );
817+
} else {
818+
dataPriv.set( this, delegateType, attaches );
819+
}
785820
} else {
786821

787822
// Return false to indicate standard teardown should be applied
@@ -797,6 +832,58 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
797832

798833
delegateType: delegateType
799834
};
835+
836+
// Support: Firefox <=44
837+
// Firefox doesn't have focus(in | out) events
838+
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
839+
//
840+
// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
841+
// focus(in | out) events fire after focus & blur events,
842+
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
843+
// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
844+
//
845+
// Support: IE 9 - 11+
846+
// To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch,
847+
// attach a single handler for both events in IE.
848+
jQuery.event.special[ delegateType ] = {
849+
setup: function() {
850+
851+
// Handle: regular nodes (via `this.ownerDocument`), window
852+
// (via `this.document`) & document (via `this`).
853+
var doc = this.ownerDocument || this.document || this,
854+
dataHolder = document.documentMode ? this : doc,
855+
attaches = dataPriv.get( dataHolder, delegateType );
856+
857+
// Support: IE 9 - 11+
858+
// We use the same native handler for focusin & focus (and focusout & blur)
859+
// so we need to coordinate setup & teardown parts between those events.
860+
// Use `delegateType` as the key as `type` is already used by `leverageNative`.
861+
if ( !attaches ) {
862+
if ( document.documentMode ) {
863+
this.addEventListener( delegateType, focusMappedHandler );
864+
} else {
865+
doc.addEventListener( type, focusMappedHandler, true );
866+
}
867+
}
868+
dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 );
869+
},
870+
teardown: function() {
871+
var doc = this.ownerDocument || this.document || this,
872+
dataHolder = document.documentMode ? this : doc,
873+
attaches = dataPriv.get( dataHolder, delegateType ) - 1;
874+
875+
if ( !attaches ) {
876+
if ( document.documentMode ) {
877+
this.removeEventListener( delegateType, focusMappedHandler );
878+
} else {
879+
doc.removeEventListener( type, focusMappedHandler, true );
880+
}
881+
dataPriv.remove( dataHolder, delegateType );
882+
} else {
883+
dataPriv.set( dataHolder, delegateType, attaches );
884+
}
885+
}
886+
};
800887
} );
801888

802889
// Create mouseenter/leave events using mouseover/out and event-time checks

src/event/focusin.js

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/event/support.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/jquery.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ define( [
1111
"./queue/delay",
1212
"./attributes",
1313
"./event",
14-
"./event/focusin",
1514
"./manipulation",
1615
"./manipulation/_evalUrl",
1716
"./wrap",

test/unit/support.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ testIframe(
6868
cors: true,
6969
createHTMLDocument: true,
7070
disconnectedMatch: true,
71-
focusin: false,
7271
getById: true,
7372
noCloneChecked: true,
7473
option: true,
@@ -93,7 +92,6 @@ testIframe(
9392
cors: false,
9493
createHTMLDocument: true,
9594
disconnectedMatch: false,
96-
focusin: true,
9795
getById: false,
9896
noCloneChecked: false,
9997
option: false,
@@ -118,7 +116,6 @@ testIframe(
118116
cors: true,
119117
createHTMLDocument: true,
120118
disconnectedMatch: true,
121-
focusin: true,
122119
getById: true,
123120
noCloneChecked: false,
124121
option: true,
@@ -143,7 +140,6 @@ testIframe(
143140
cors: true,
144141
createHTMLDocument: true,
145142
disconnectedMatch: true,
146-
focusin: false,
147143
getById: true,
148144
noCloneChecked: true,
149145
option: true,
@@ -168,7 +164,6 @@ testIframe(
168164
cors: true,
169165
createHTMLDocument: true,
170166
disconnectedMatch: true,
171-
focusin: false,
172167
getById: true,
173168
noCloneChecked: true,
174169
option: true,
@@ -193,7 +188,6 @@ testIframe(
193188
cors: true,
194189
createHTMLDocument: true,
195190
disconnectedMatch: true,
196-
focusin: false,
197191
getById: true,
198192
noCloneChecked: true,
199193
option: true,
@@ -218,7 +212,6 @@ testIframe(
218212
cors: true,
219213
createHTMLDocument: true,
220214
disconnectedMatch: true,
221-
focusin: false,
222215
getById: true,
223216
noCloneChecked: true,
224217
option: true,
@@ -243,7 +236,6 @@ testIframe(
243236
cors: true,
244237
createHTMLDocument: true,
245238
disconnectedMatch: true,
246-
focusin: false,
247239
getById: true,
248240
noCloneChecked: true,
249241
option: true,
@@ -268,7 +260,6 @@ testIframe(
268260
cors: true,
269261
createHTMLDocument: true,
270262
disconnectedMatch: true,
271-
focusin: false,
272263
getById: true,
273264
noCloneChecked: true,
274265
option: true,
@@ -293,7 +284,6 @@ testIframe(
293284
cors: true,
294285
createHTMLDocument: true,
295286
disconnectedMatch: true,
296-
focusin: false,
297287
getById: true,
298288
noCloneChecked: true,
299289
option: true,
@@ -318,7 +308,6 @@ testIframe(
318308
cors: true,
319309
createHTMLDocument: false,
320310
disconnectedMatch: true,
321-
focusin: false,
322311
getById: true,
323312
noCloneChecked: true,
324313
option: true,
@@ -343,7 +332,6 @@ testIframe(
343332
cors: true,
344333
createHTMLDocument: true,
345334
disconnectedMatch: true,
346-
focusin: false,
347335
getById: true,
348336
noCloneChecked: true,
349337
option: true,
@@ -368,7 +356,6 @@ testIframe(
368356
cors: true,
369357
createHTMLDocument: true,
370358
disconnectedMatch: true,
371-
focusin: false,
372359
getById: true,
373360
noCloneChecked: true,
374361
option: true,
@@ -393,7 +380,6 @@ testIframe(
393380
cors: true,
394381
createHTMLDocument: true,
395382
disconnectedMatch: true,
396-
focusin: false,
397383
getById: true,
398384
noCloneChecked: true,
399385
option: true,
@@ -418,7 +404,6 @@ testIframe(
418404
cors: true,
419405
createHTMLDocument: true,
420406
disconnectedMatch: true,
421-
focusin: false,
422407
getById: true,
423408
noCloneChecked: true,
424409
option: true,

0 commit comments

Comments
 (0)