Skip to content

Commit 1cecf64

Browse files
committed
Fix #14180. Allow cross-frame use of focusin/out. Close gh-1369.
1 parent b7f62ab commit 1cecf64

File tree

3 files changed

+68
-7
lines changed

3 files changed

+68
-7
lines changed

src/event.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -726,21 +726,31 @@ jQuery.each({
726726
if ( !support.focusinBubbles ) {
727727
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
728728

729-
// Attach a single capturing handler while someone wants focusin/focusout
730-
var attaches = 0,
731-
handler = function( event ) {
729+
// Attach a single capturing handler on the document while someone wants focusin/focusout
730+
var handler = function( event ) {
732731
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
733732
};
734733

735734
jQuery.event.special[ fix ] = {
736735
setup: function() {
737-
if ( attaches++ === 0 ) {
738-
document.addEventListener( orig, handler, true );
736+
var doc = this.ownerDocument,
737+
attaches = data_priv.access( doc, fix );
738+
739+
if ( !attaches ) {
740+
doc.addEventListener( orig, handler, true );
739741
}
742+
data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
740743
},
741744
teardown: function() {
742-
if ( --attaches === 0 ) {
743-
document.removeEventListener( orig, handler, true );
745+
var doc = this.ownerDocument,
746+
attaches = data_priv.access( doc, fix ) - 1;
747+
748+
if ( !attaches ) {
749+
doc.removeEventListener( orig, handler, true );
750+
data_priv.remove( doc, fix );
751+
752+
} else {
753+
data_priv.access( doc, fix, attaches );
744754
}
745755
}
746756
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
5+
<title>focusin event cross-frame (#14180)</title>
6+
7+
<script src="../../jquery.js"></script>
8+
</head>
9+
<body>
10+
<input type="text" id="frame-input" />
11+
<script>
12+
// Call parent when this frame is fully loaded, it will mess with #frame-input
13+
jQuery( window ).one( "load", function() {
14+
window.parent.iframeCallback( document );
15+
});
16+
</script>
17+
</body>
18+
</html>

test/unit/event.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,39 @@ test("fixHooks extensions", function() {
23742374
jQuery.event.fixHooks.click = saved;
23752375
});
23762376

2377+
testIframeWithCallback( "focusin from an iframe", "event/focusinCrossFrame.html", function( frameDoc ) {
2378+
expect(1);
2379+
2380+
var input = jQuery( frameDoc ).find( "#frame-input" );
2381+
2382+
if ( input.length ) {
2383+
// Create a focusin handler on the parent; shouldn't affect the iframe's fate
2384+
jQuery ( "body" ).on( "focusin.iframeTest", function() {
2385+
ok( false, "fired a focusin event in the parent document" );
2386+
});
2387+
2388+
input.on( "focusin", function() {
2389+
ok( true, "fired a focusin event in the iframe" );
2390+
});
2391+
2392+
// Avoid a native event; Chrome can't force focus to another frame
2393+
input.trigger( "focusin" );
2394+
2395+
// Must manually remove handler to avoid leaks in our data store
2396+
input.remove();
2397+
2398+
// Be sure it was removed; nothing should happen
2399+
input.trigger( "focusin" );
2400+
2401+
// Remove body handler manually since it's outside the fixture
2402+
jQuery( "body" ).off( "focusin.iframeTest" );
2403+
2404+
} else {
2405+
// Opera 12 (pre-Blink) doesn't select anything
2406+
ok( true, "SOFTFAIL: no focus event fired in the iframe" );
2407+
}
2408+
});
2409+
23772410
testIframeWithCallback( "jQuery.ready promise", "event/promiseReady.html", function( isOk ) {
23782411
expect(1);
23792412
ok( isOk, "$.when( $.ready ) works" );

0 commit comments

Comments
 (0)