mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Event: Simulate focus/blur in IE via focusin/focusout
In IE (all versions), `focus` & `blur` handlers are fired asynchronously but `focusin` & `focusout` are run synchronously. In other browsers, all those handlers are fired synchronously. Asynchronous behavior of these handlers in IE caused issues for IE (gh-4856, gh-4859). We now simulate `focus` via `focusin` & `blur` via `focusout` in IE to avoid these issues. This also let us simplify some tests. This commit also simplifies `leverageNative` - with IE now using `focusin` to simulate `focus` and `focusout` to simulate `blur`, we don't have to deal with async events in `leverageNative`. This also fixes broken `focus` triggers after first triggering it on a hidden element - previously, `leverageNative` assumed that the native `focus` handler not firing after calling the native `focus` method meant it would be handled later, asynchronously, which was not the case (gh-4950). Fixes gh-4856 Fixes gh-4859 Fixes gh-4950 Closes gh-5223 Co-authored-by: Richard Gibson <richard.gibson@gmail.com>
This commit is contained in:
parent
992a1911d0
commit
ce60d31893
102
src/event.js
102
src/event.js
@ -1,9 +1,9 @@
|
|||||||
import jQuery from "./core.js";
|
import jQuery from "./core.js";
|
||||||
import document from "./var/document.js";
|
|
||||||
import documentElement from "./var/documentElement.js";
|
import documentElement from "./var/documentElement.js";
|
||||||
import rnothtmlwhite from "./var/rnothtmlwhite.js";
|
import rnothtmlwhite from "./var/rnothtmlwhite.js";
|
||||||
import rcheckableType from "./var/rcheckableType.js";
|
import rcheckableType from "./var/rcheckableType.js";
|
||||||
import slice from "./var/slice.js";
|
import slice from "./var/slice.js";
|
||||||
|
import isIE from "./var/isIE.js";
|
||||||
import acceptData from "./data/var/acceptData.js";
|
import acceptData from "./data/var/acceptData.js";
|
||||||
import dataPriv from "./data/var/dataPriv.js";
|
import dataPriv from "./data/var/dataPriv.js";
|
||||||
import nodeName from "./core/nodeName.js";
|
import nodeName from "./core/nodeName.js";
|
||||||
@ -21,16 +21,6 @@ function returnFalse() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support: IE <=9 - 11+
|
|
||||||
// focus() and blur() are asynchronous, except when they are no-op.
|
|
||||||
// So expect focus to be synchronous when the element is already active,
|
|
||||||
// and blur to be synchronous when the element is not already active.
|
|
||||||
// (focus and blur are always synchronous in other supported browsers,
|
|
||||||
// this just defines when we can count on it).
|
|
||||||
function expectSync( elem, type ) {
|
|
||||||
return ( elem === document.activeElement ) === ( type === "focus" );
|
|
||||||
}
|
|
||||||
|
|
||||||
function on( elem, types, selector, data, fn, one ) {
|
function on( elem, types, selector, data, fn, one ) {
|
||||||
var origFn, type;
|
var origFn, type;
|
||||||
|
|
||||||
@ -459,7 +449,7 @@ jQuery.event = {
|
|||||||
el.click && nodeName( el, "input" ) ) {
|
el.click && nodeName( el, "input" ) ) {
|
||||||
|
|
||||||
// dataPriv.set( el, "click", ... )
|
// dataPriv.set( el, "click", ... )
|
||||||
leverageNative( el, "click", returnTrue );
|
leverageNative( el, "click", true );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return false to allow normal processing in the caller
|
// Return false to allow normal processing in the caller
|
||||||
@ -511,10 +501,10 @@ jQuery.event = {
|
|||||||
// synthetic events by interrupting progress until reinvoked in response to
|
// synthetic events by interrupting progress until reinvoked in response to
|
||||||
// *native* events that it fires directly, ensuring that state changes have
|
// *native* events that it fires directly, ensuring that state changes have
|
||||||
// already occurred before other listeners are invoked.
|
// already occurred before other listeners are invoked.
|
||||||
function leverageNative( el, type, expectSync ) {
|
function leverageNative( el, type, isSetup ) {
|
||||||
|
|
||||||
// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
|
// Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add
|
||||||
if ( !expectSync ) {
|
if ( !isSetup ) {
|
||||||
if ( dataPriv.get( el, type ) === undefined ) {
|
if ( dataPriv.get( el, type ) === undefined ) {
|
||||||
jQuery.event.add( el, type, returnTrue );
|
jQuery.event.add( el, type, returnTrue );
|
||||||
}
|
}
|
||||||
@ -526,15 +516,13 @@ function leverageNative( el, type, expectSync ) {
|
|||||||
jQuery.event.add( el, type, {
|
jQuery.event.add( el, type, {
|
||||||
namespace: false,
|
namespace: false,
|
||||||
handler: function( event ) {
|
handler: function( event ) {
|
||||||
var notAsync, result,
|
var result,
|
||||||
saved = dataPriv.get( this, type );
|
saved = dataPriv.get( this, type );
|
||||||
|
|
||||||
if ( ( event.isTrigger & 1 ) && this[ type ] ) {
|
if ( ( event.isTrigger & 1 ) && this[ type ] ) {
|
||||||
|
|
||||||
// Interrupt processing of the outer synthetic .trigger()ed event
|
// Interrupt processing of the outer synthetic .trigger()ed event
|
||||||
// Saved data should be false in such cases, but might be a leftover capture object
|
if ( !saved ) {
|
||||||
// from an async native handler (gh-4350)
|
|
||||||
if ( !saved.length ) {
|
|
||||||
|
|
||||||
// Store arguments for use when handling the inner native event
|
// Store arguments for use when handling the inner native event
|
||||||
// There will always be at least one argument (an event object), so this array
|
// There will always be at least one argument (an event object), so this array
|
||||||
@ -543,28 +531,17 @@ function leverageNative( el, type, expectSync ) {
|
|||||||
dataPriv.set( this, type, saved );
|
dataPriv.set( this, type, saved );
|
||||||
|
|
||||||
// Trigger the native event and capture its result
|
// Trigger the native event and capture its result
|
||||||
// Support: IE <=9 - 11+
|
|
||||||
// focus() and blur() are asynchronous
|
|
||||||
notAsync = expectSync( this, type );
|
|
||||||
this[ type ]();
|
this[ type ]();
|
||||||
result = dataPriv.get( this, type );
|
result = dataPriv.get( this, type );
|
||||||
if ( saved !== result || notAsync ) {
|
dataPriv.set( this, type, false );
|
||||||
dataPriv.set( this, type, false );
|
|
||||||
} else {
|
|
||||||
result = {};
|
|
||||||
}
|
|
||||||
if ( saved !== result ) {
|
if ( saved !== result ) {
|
||||||
|
|
||||||
// Cancel the outer synthetic event
|
// Cancel the outer synthetic event
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Support: Chrome 86+
|
return result;
|
||||||
// In Chrome, if an element having a focusout handler is blurred by
|
|
||||||
// clicking outside of it, it invokes the handler synchronously. If
|
|
||||||
// that handler calls `.remove()` on the element, the data is cleared,
|
|
||||||
// leaving `result` undefined. We need to guard against this.
|
|
||||||
return result && result.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is an inner synthetic event for an event with a bubbling surrogate
|
// If this is an inner synthetic event for an event with a bubbling surrogate
|
||||||
@ -582,16 +559,11 @@ function leverageNative( el, type, expectSync ) {
|
|||||||
} else if ( saved.length ) {
|
} else if ( saved.length ) {
|
||||||
|
|
||||||
// ...and capture the result
|
// ...and capture the result
|
||||||
dataPriv.set( this, type, {
|
dataPriv.set( this, type, jQuery.event.trigger(
|
||||||
value: jQuery.event.trigger(
|
saved[ 0 ],
|
||||||
|
saved.slice( 1 ),
|
||||||
// Support: IE <=9 - 11+
|
this
|
||||||
// Extend with the prototype to reset the above stopImmediatePropagation()
|
) );
|
||||||
jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
|
|
||||||
saved.slice( 1 ),
|
|
||||||
this
|
|
||||||
)
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Abort handling of the native event
|
// Abort handling of the native event
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
@ -724,6 +696,29 @@ jQuery.each( {
|
|||||||
}, jQuery.event.addProp );
|
}, jQuery.event.addProp );
|
||||||
|
|
||||||
jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
|
jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
|
||||||
|
|
||||||
|
// Support: IE 11+
|
||||||
|
// Attach a single focusin/focusout handler on the document while someone wants focus/blur.
|
||||||
|
// This is because the former are synchronous in IE while the latter are async. In other
|
||||||
|
// browsers, all those handlers are invoked synchronously.
|
||||||
|
function focusMappedHandler( nativeEvent ) {
|
||||||
|
|
||||||
|
// `eventHandle` would already wrap the event, but we need to change the `type` here.
|
||||||
|
var event = jQuery.event.fix( nativeEvent );
|
||||||
|
event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
|
||||||
|
event.isSimulated = true;
|
||||||
|
|
||||||
|
// focus/blur don't bubble while focusin/focusout do; simulate the former by only
|
||||||
|
// invoking the handler at the lower level.
|
||||||
|
if ( event.target === event.currentTarget ) {
|
||||||
|
|
||||||
|
// The setup part calls `leverageNative`, which, in turn, calls
|
||||||
|
// `jQuery.event.add`, so event handle will already have been set
|
||||||
|
// by this point.
|
||||||
|
dataPriv.get( this, "handle" )( event );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jQuery.event.special[ type ] = {
|
jQuery.event.special[ type ] = {
|
||||||
|
|
||||||
// Utilize native event if possible so blur/focus sequence is correct
|
// Utilize native event if possible so blur/focus sequence is correct
|
||||||
@ -732,10 +727,15 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
|
|||||||
// Claim the first handler
|
// Claim the first handler
|
||||||
// dataPriv.set( this, "focus", ... )
|
// dataPriv.set( this, "focus", ... )
|
||||||
// dataPriv.set( this, "blur", ... )
|
// dataPriv.set( this, "blur", ... )
|
||||||
leverageNative( this, type, expectSync );
|
leverageNative( this, type, true );
|
||||||
|
|
||||||
// Return false to allow normal processing in the caller
|
if ( isIE ) {
|
||||||
return false;
|
this.addEventListener( delegateType, focusMappedHandler );
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Return false to allow normal processing in the caller
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
trigger: function() {
|
trigger: function() {
|
||||||
|
|
||||||
@ -746,6 +746,16 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
teardown: function() {
|
||||||
|
if ( isIE ) {
|
||||||
|
this.removeEventListener( delegateType, focusMappedHandler );
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Return false to indicate standard teardown should be applied
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Suppress native focus or blur if we're currently inside
|
// Suppress native focus or blur if we're currently inside
|
||||||
// a leveraged native-event stack
|
// a leveraged native-event stack
|
||||||
_default: function( event ) {
|
_default: function( event ) {
|
||||||
|
@ -2172,12 +2172,12 @@ QUnit.test( "focusin bubbles", function( assert ) {
|
|||||||
|
|
||||||
// Removed since DOM focus is unreliable on test swarm
|
// Removed since DOM focus is unreliable on test swarm
|
||||||
// DOM focus method
|
// DOM focus method
|
||||||
// input[0].focus();
|
// input[ 0 ].focus();
|
||||||
|
|
||||||
// To make the next focus test work, we need to take focus off the input.
|
// To make the next focus test work, we need to take focus off the input.
|
||||||
// This will fire another focusin event, so set order to reflect that.
|
// This will fire another focusin event, so set order to reflect that.
|
||||||
// order = 1;
|
// order = 1;
|
||||||
// jQuery("#text1")[0].focus();
|
// jQuery( "#text1" )[ 0 ].focus();
|
||||||
|
|
||||||
// jQuery trigger, which calls DOM focus
|
// jQuery trigger, which calls DOM focus
|
||||||
order = 0;
|
order = 0;
|
||||||
@ -2187,6 +2187,42 @@ QUnit.test( "focusin bubbles", function( assert ) {
|
|||||||
jQuery( "body" ).off( "focusin.focusinBubblesTest" );
|
jQuery( "body" ).off( "focusin.focusinBubblesTest" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
QUnit.test( "focus does not bubble", function( assert ) {
|
||||||
|
assert.expect( 1 );
|
||||||
|
|
||||||
|
var done = assert.async(),
|
||||||
|
input = jQuery( "<input type='text' />" ).prependTo( "body" );
|
||||||
|
|
||||||
|
// focus the element so DOM focus won't fire
|
||||||
|
input[ 0 ].focus();
|
||||||
|
|
||||||
|
jQuery( "body" ).on( "focus.focusDoesNotBubbleTest", function() {
|
||||||
|
assert.ok( false, "focus doesn't fire on body" );
|
||||||
|
} );
|
||||||
|
|
||||||
|
input.on( "focus.focusDoesNotBubbleTest", function() {
|
||||||
|
assert.ok( true, "focus on the element" );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Removed since DOM focus is unreliable on test swarm
|
||||||
|
// DOM focus method
|
||||||
|
// input[ 0 ].focus();
|
||||||
|
|
||||||
|
// To make the next focus test work, we need to take focus off the input.
|
||||||
|
// This will fire another focusin event, so set order to reflect that.
|
||||||
|
// jQuery( "#text1" )[ 0 ].focus();
|
||||||
|
|
||||||
|
// jQuery trigger, which calls DOM focus
|
||||||
|
input.trigger( "focus" );
|
||||||
|
|
||||||
|
input.remove();
|
||||||
|
jQuery( "body" ).off( "focus.focusDoesNotBubbleTest" );
|
||||||
|
|
||||||
|
setTimeout( function() {
|
||||||
|
done();
|
||||||
|
}, 50 );
|
||||||
|
} );
|
||||||
|
|
||||||
QUnit.test( "custom events with colons (trac-3533, trac-8272)", function( assert ) {
|
QUnit.test( "custom events with colons (trac-3533, trac-8272)", function( assert ) {
|
||||||
assert.expect( 1 );
|
assert.expect( 1 );
|
||||||
|
|
||||||
@ -2652,6 +2688,10 @@ QUnit.test( "element removed during focusout (gh-4417)", function( assert ) {
|
|||||||
button[ 0 ].blur = function() {
|
button[ 0 ].blur = function() {
|
||||||
jQuery.cleanData( [ this ] );
|
jQuery.cleanData( [ this ] );
|
||||||
this.parentNode.removeChild( this );
|
this.parentNode.removeChild( this );
|
||||||
|
|
||||||
|
// Redefine `blur` to avoid a hard crash in Karma tests that stop
|
||||||
|
// the test runner in case this test fails.
|
||||||
|
this.blur = jQuery.noop;
|
||||||
};
|
};
|
||||||
|
|
||||||
button[ 0 ].click();
|
button[ 0 ].click();
|
||||||
@ -3067,12 +3107,7 @@ QUnit.test( "focusout/focusin support", function( assert ) {
|
|||||||
var focus,
|
var focus,
|
||||||
parent = jQuery( "<div>" ),
|
parent = jQuery( "<div>" ),
|
||||||
input = jQuery( "<input>" ),
|
input = jQuery( "<input>" ),
|
||||||
inputExternal = jQuery( "<input>" ),
|
inputExternal = jQuery( "<input>" );
|
||||||
|
|
||||||
// Support: IE <=9 - 11+
|
|
||||||
// focus and blur events are asynchronous; this is the resulting mess.
|
|
||||||
// The browser window must be topmost for this to work properly!!
|
|
||||||
done = assert.async();
|
|
||||||
|
|
||||||
parent.append( input );
|
parent.append( input );
|
||||||
jQuery( "#qunit-fixture" ).append( parent ).append( inputExternal );
|
jQuery( "#qunit-fixture" ).append( parent ).append( inputExternal );
|
||||||
@ -3080,61 +3115,54 @@ QUnit.test( "focusout/focusin support", function( assert ) {
|
|||||||
// initially, lose focus
|
// initially, lose focus
|
||||||
inputExternal[ 0 ].focus();
|
inputExternal[ 0 ].focus();
|
||||||
|
|
||||||
setTimeout( function() {
|
parent
|
||||||
parent
|
.on( "focus", function() {
|
||||||
.on( "focus", function() {
|
assert.ok( false, "parent: focus not fired" );
|
||||||
assert.ok( false, "parent: focus not fired" );
|
} )
|
||||||
} )
|
.on( "focusin", function() {
|
||||||
.on( "focusin", function() {
|
assert.ok( true, "parent: focusin fired" );
|
||||||
assert.ok( true, "parent: focusin fired" );
|
} )
|
||||||
} )
|
.on( "blur", function() {
|
||||||
.on( "blur", function() {
|
assert.ok( false, "parent: blur not fired" );
|
||||||
assert.ok( false, "parent: blur not fired" );
|
} )
|
||||||
} )
|
.on( "focusout", function() {
|
||||||
.on( "focusout", function() {
|
assert.ok( true, "parent: focusout fired" );
|
||||||
assert.ok( true, "parent: focusout fired" );
|
} );
|
||||||
} );
|
|
||||||
|
|
||||||
input
|
input
|
||||||
.on( "focus", function() {
|
.on( "focus", function() {
|
||||||
assert.ok( true, "element: focus fired" );
|
assert.ok( true, "element: focus fired" );
|
||||||
} )
|
} )
|
||||||
.on( "focusin", function() {
|
.on( "focusin", function() {
|
||||||
assert.ok( true, "element: focusin fired" );
|
assert.ok( true, "element: focusin fired" );
|
||||||
focus = true;
|
focus = true;
|
||||||
} )
|
} )
|
||||||
.on( "blur", function() {
|
.on( "blur", function() {
|
||||||
assert.ok( true, "parent: blur fired" );
|
assert.ok( true, "parent: blur fired" );
|
||||||
} )
|
} )
|
||||||
.on( "focusout", function() {
|
.on( "focusout", function() {
|
||||||
assert.ok( true, "element: focusout fired" );
|
assert.ok( true, "element: focusout fired" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// gain focus
|
// gain focus
|
||||||
input[ 0 ].focus();
|
input[ 0 ].focus();
|
||||||
|
|
||||||
// then lose it
|
// then lose it
|
||||||
inputExternal[ 0 ].focus();
|
inputExternal[ 0 ].focus();
|
||||||
|
|
||||||
setTimeout( function() {
|
// DOM focus is unreliable in TestSwarm
|
||||||
|
if ( QUnit.isSwarm && !focus ) {
|
||||||
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
|
}
|
||||||
|
|
||||||
// DOM focus is unreliable in TestSwarm
|
// cleanup
|
||||||
if ( QUnit.isSwarm && !focus ) {
|
parent.off();
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
input.off();
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
parent.off();
|
|
||||||
input.off();
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, 50 );
|
|
||||||
}, 50 );
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "focus-blur order (trac-12868)", function( assert ) {
|
QUnit.test( "focus-blur order (trac-12868)", function( assert ) {
|
||||||
@ -3142,56 +3170,45 @@ QUnit.test( "focus-blur order (trac-12868)", function( assert ) {
|
|||||||
|
|
||||||
var order,
|
var order,
|
||||||
$text = jQuery( "#text1" ),
|
$text = jQuery( "#text1" ),
|
||||||
$radio = jQuery( "#radio1" ),
|
$radio = jQuery( "#radio1" );
|
||||||
|
|
||||||
// Support: IE <=9 - 11+
|
|
||||||
// focus and blur events are asynchronous; this is the resulting mess.
|
|
||||||
// The browser window must be topmost for this to work properly!!
|
|
||||||
done = assert.async();
|
|
||||||
|
|
||||||
$radio[ 0 ].focus();
|
$radio[ 0 ].focus();
|
||||||
|
|
||||||
setTimeout( function() {
|
$text
|
||||||
|
.on( "focus", function() {
|
||||||
|
assert.equal( order++, 1, "text focus" );
|
||||||
|
} )
|
||||||
|
.on( "blur", function() {
|
||||||
|
assert.equal( order++, 0, "text blur" );
|
||||||
|
} );
|
||||||
|
$radio
|
||||||
|
.on( "focus", function() {
|
||||||
|
assert.equal( order++, 1, "radio focus" );
|
||||||
|
} )
|
||||||
|
.on( "blur", function() {
|
||||||
|
assert.equal( order++, 0, "radio blur" );
|
||||||
|
} );
|
||||||
|
|
||||||
$text
|
// Enabled input getting focus
|
||||||
.on( "focus", function() {
|
order = 0;
|
||||||
assert.equal( order++, 1, "text focus" );
|
assert.equal( document.activeElement, $radio[ 0 ], "radio has focus" );
|
||||||
} )
|
$text.trigger( "focus" );
|
||||||
.on( "blur", function() {
|
|
||||||
assert.equal( order++, 0, "text blur" );
|
|
||||||
} );
|
|
||||||
$radio
|
|
||||||
.on( "focus", function() {
|
|
||||||
assert.equal( order++, 1, "radio focus" );
|
|
||||||
} )
|
|
||||||
.on( "blur", function() {
|
|
||||||
assert.equal( order++, 0, "radio blur" );
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Enabled input getting focus
|
// DOM focus is unreliable in TestSwarm
|
||||||
order = 0;
|
if ( QUnit.isSwarm && order === 0 ) {
|
||||||
assert.equal( document.activeElement, $radio[ 0 ], "radio has focus" );
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
$text.trigger( "focus" );
|
assert.ok( true, "GAP: Could not observe focus change" );
|
||||||
setTimeout( function() {
|
}
|
||||||
|
|
||||||
// DOM focus is unreliable in TestSwarm
|
assert.equal( document.activeElement, $text[ 0 ], "text has focus" );
|
||||||
if ( QUnit.isSwarm && order === 0 ) {
|
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
assert.ok( true, "GAP: Could not observe focus change" );
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.equal( document.activeElement, $text[ 0 ], "text has focus" );
|
// Run handlers without native method on an input
|
||||||
|
order = 1;
|
||||||
|
$radio.triggerHandler( "focus" );
|
||||||
|
|
||||||
// Run handlers without native method on an input
|
// Clean up
|
||||||
order = 1;
|
$text.off();
|
||||||
$radio.triggerHandler( "focus" );
|
$radio.off();
|
||||||
|
|
||||||
// Clean up
|
|
||||||
$text.off();
|
|
||||||
$radio.off();
|
|
||||||
done();
|
|
||||||
}, 50 );
|
|
||||||
}, 50 );
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "Event handling works with multiple async focus events (gh-4350)", function( assert ) {
|
QUnit.test( "Event handling works with multiple async focus events (gh-4350)", function( assert ) {
|
||||||
@ -3199,10 +3216,6 @@ QUnit.test( "Event handling works with multiple async focus events (gh-4350)", f
|
|||||||
|
|
||||||
var remaining = 3,
|
var remaining = 3,
|
||||||
input = jQuery( "#name" ),
|
input = jQuery( "#name" ),
|
||||||
|
|
||||||
// Support: IE <=9 - 11+
|
|
||||||
// focus and blur events are asynchronous; this is the resulting mess.
|
|
||||||
// The browser window must be topmost for this to work properly!!
|
|
||||||
done = assert.async();
|
done = assert.async();
|
||||||
|
|
||||||
input
|
input
|
||||||
@ -3212,6 +3225,17 @@ QUnit.test( "Event handling works with multiple async focus events (gh-4350)", f
|
|||||||
if ( remaining > 0 ) {
|
if ( remaining > 0 ) {
|
||||||
input.trigger( "blur" );
|
input.trigger( "blur" );
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
if ( QUnit.isIE ) {
|
||||||
|
|
||||||
|
// Support: <=IE 11+
|
||||||
|
// In IE, one of the blurs sometimes triggers a focus on body
|
||||||
|
// which in turn restores focus to the input, leading to 4 assertions
|
||||||
|
// firing instead of three. This only happens if other tests are
|
||||||
|
// running on the same test page. Avoid this issue in tests by removing
|
||||||
|
// the handler early.
|
||||||
|
input.off( "focus" );
|
||||||
|
}
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
} )
|
} )
|
||||||
@ -3237,6 +3261,45 @@ QUnit.test( "Event handling works with multiple async focus events (gh-4350)", f
|
|||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// Support: IE <=9 - 11+
|
||||||
|
// focus and blur events are asynchronous.
|
||||||
|
// The browser window must be topmost for this to work properly!!
|
||||||
|
QUnit.test( "async focus queues properly (gh-4859)", function( assert ) {
|
||||||
|
assert.expect( 1 );
|
||||||
|
|
||||||
|
var $text = jQuery( "#text1" ),
|
||||||
|
$radio = jQuery( "#radio1" ),
|
||||||
|
done = assert.async();
|
||||||
|
|
||||||
|
$text.trigger( "focus" );
|
||||||
|
$radio.trigger( "focus" );
|
||||||
|
$text.trigger( "focus" );
|
||||||
|
|
||||||
|
setTimeout( function() {
|
||||||
|
assert.equal( document.activeElement, $text[ 0 ], "focus follows the last trigger" );
|
||||||
|
done();
|
||||||
|
}, 500 );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Support: IE <=9 - 11+
|
||||||
|
// focus and blur events are asynchronous.
|
||||||
|
// The browser window must be topmost for this to work properly!!
|
||||||
|
QUnit.test( "async focus queues properly with blur (gh-4856)", function( assert ) {
|
||||||
|
assert.expect( 1 );
|
||||||
|
|
||||||
|
var $text = jQuery( "#text1" ),
|
||||||
|
done = assert.async();
|
||||||
|
|
||||||
|
$text.trigger( "focus" );
|
||||||
|
$text.trigger( "blur" );
|
||||||
|
$text.trigger( "focus" );
|
||||||
|
|
||||||
|
setTimeout( function() {
|
||||||
|
assert.equal( document.activeElement, $text[ 0 ], "focus-after-blur is respected" );
|
||||||
|
done();
|
||||||
|
}, 500 );
|
||||||
|
} );
|
||||||
|
|
||||||
QUnit.test( "native-backed events preserve trigger data (gh-1741, gh-4139)", function( assert ) {
|
QUnit.test( "native-backed events preserve trigger data (gh-1741, gh-4139)", function( assert ) {
|
||||||
assert.expect( 17 );
|
assert.expect( 17 );
|
||||||
|
|
||||||
@ -3246,12 +3309,7 @@ QUnit.test( "native-backed events preserve trigger data (gh-1741, gh-4139)", fun
|
|||||||
targets = jQuery( parent[ 0 ].childNodes ),
|
targets = jQuery( parent[ 0 ].childNodes ),
|
||||||
checkbox = jQuery( targets[ 0 ] ),
|
checkbox = jQuery( targets[ 0 ] ),
|
||||||
data = [ "arg1", "arg2" ],
|
data = [ "arg1", "arg2" ],
|
||||||
slice = data.slice,
|
slice = data.slice;
|
||||||
|
|
||||||
// Support: IE <=9 - 11+
|
|
||||||
// focus and blur events are asynchronous; this is the resulting mess.
|
|
||||||
// The browser window must be topmost for this to work properly!!
|
|
||||||
done = assert.async();
|
|
||||||
|
|
||||||
// click (gh-4139)
|
// click (gh-4139)
|
||||||
assert.strictEqual( targets[ 0 ].checked, false, "checkbox unchecked before click" );
|
assert.strictEqual( targets[ 0 ].checked, false, "checkbox unchecked before click" );
|
||||||
@ -3277,18 +3335,26 @@ QUnit.test( "native-backed events preserve trigger data (gh-1741, gh-4139)", fun
|
|||||||
var type = event.type;
|
var type = event.type;
|
||||||
assert.deepEqual( slice.call( arguments, 1 ), data,
|
assert.deepEqual( slice.call( arguments, 1 ), data,
|
||||||
type + " handler received correct data" );
|
type + " handler received correct data" );
|
||||||
|
|
||||||
|
if ( QUnit.isIE && type === "focus" ) {
|
||||||
|
|
||||||
|
// Support: <=IE 11+
|
||||||
|
// In IE, one of the blurs sometimes triggers a focus on body
|
||||||
|
// which in turn restores focus to the input, leading to 4 assertions
|
||||||
|
// firing instead of three. This only happens if other tests are
|
||||||
|
// running on the same test page. Avoid this issue in tests by removing
|
||||||
|
// the handler early.
|
||||||
|
checkbox.off( "focus" );
|
||||||
|
}
|
||||||
} );
|
} );
|
||||||
checkbox.trigger( "focus", data );
|
checkbox.trigger( "focus", data );
|
||||||
setTimeout( function() {
|
|
||||||
assert.strictEqual( document.activeElement, checkbox[ 0 ],
|
assert.strictEqual( document.activeElement, checkbox[ 0 ],
|
||||||
"element focused after focus event (default action)" );
|
"element focused after focus event (default action)" );
|
||||||
checkbox.trigger( "blur", data );
|
checkbox.trigger( "blur", data );
|
||||||
setTimeout( function() {
|
|
||||||
assert.notEqual( document.activeElement, checkbox[ 0 ],
|
assert.notEqual( document.activeElement, checkbox[ 0 ],
|
||||||
"element not focused after blur event (default action)" );
|
"element not focused after blur event (default action)" );
|
||||||
done();
|
|
||||||
}, 50 );
|
|
||||||
}, 50 );
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( "focus change during a focus handler (gh-4382)", function( assert ) {
|
QUnit.test( "focus change during a focus handler (gh-4382)", function( assert ) {
|
||||||
@ -3341,6 +3407,22 @@ QUnit.test( "trigger(focus) works after .on(focus).off(focus) (gh-4867)", functi
|
|||||||
assert.equal( document.activeElement, input[ 0 ], "input has focus" );
|
assert.equal( document.activeElement, input[ 0 ], "input has focus" );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
QUnit.test( "trigger(focus) works after focusing when hidden (gh-4950)", function( assert ) {
|
||||||
|
assert.expect( 1 );
|
||||||
|
|
||||||
|
var input = jQuery( "<input />" );
|
||||||
|
|
||||||
|
input.appendTo( "#qunit-fixture" );
|
||||||
|
|
||||||
|
input
|
||||||
|
.css( "display", "none" )
|
||||||
|
.trigger( "focus" )
|
||||||
|
.css( "display", "" )
|
||||||
|
.trigger( "focus" );
|
||||||
|
|
||||||
|
assert.equal( document.activeElement, input[ 0 ], "input has focus" );
|
||||||
|
} );
|
||||||
|
|
||||||
// TODO replace with an adaptation of
|
// TODO replace with an adaptation of
|
||||||
// https://github.com/jquery/jquery/pull/1367/files#diff-a215316abbaabdf71857809e8673ea28R2464
|
// https://github.com/jquery/jquery/pull/1367/files#diff-a215316abbaabdf71857809e8673ea28R2464
|
||||||
( function() {
|
( function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user