You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
jQuery v3.6.0 breaks Bootstrap's test suite in IE #4859
Opening the issue after quickly chatting with mgol on IRC
I made a branch to update jQuery to v3.6.0 in twbs/bootstrap#33243. I notice our BrowserStack tests fail consistently on Internet Explorer 10 and 11. v3.5.1 works fine.
git clone https://github.com/twbs/bootstrap.git -b v4-dev-xmr-update-jquery
npm i
npm run js-test-cloud #assumes you have BrowserStack credentials set up
@mgol The idea discussed in today's weekly meeting:
// Ensure the presence of an event listener that handles manually-triggered
// synthetic events by interrupting progress until reinvoked in response to
// *native* events that it fires directly, ensuring that state changes have
// already occurred before other listeners are invoked.
function leverageNative( el, type, expectSync ) {
+ var result = {}, queue = [];
// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
if ( !expectSync ) {
if ( dataPriv.get( el, type ) === undefined ) {
jQuery.event.add( el, type, returnTrue );
}
return;
}
// Register the controller as a special universal handler for all event namespaces
dataPriv.set( el, type, false );
jQuery.event.add( el, type, {
namespace: false,
handler: function( event ) {
- var notAsync, result,- saved = dataPriv.get( this, type );+ var forceSync,+ i = 0,+ length = queue.length,+ oldResult = result;
if ( ( event.isTrigger & 1 ) && this[ type ] ) {
// Interrupt processing of the outer synthetic .trigger()ed event
- // Saved data should be false in such cases, but might be a leftover capture object- // from an async native handler (gh-4350)- if ( !saved.length ) {+ if ( !event.isInner ) {
// Store arguments for use when handling the inner native event
- // There will always be at least one argument (an event object), so this array- // will not be confused with a leftover capture object.- saved = slice.call( arguments );- dataPriv.set( this, type, saved );+ queue.push( slice.call( arguments ) );
// Trigger the native event and capture its result
// Support: IE <=9 - 11+
// focus() and blur() are asynchronous
- notAsync = expectSync( this, type );+ forceSync = expectSync( this, type );
this[ type ]();
- result = dataPriv.get( this, type );- if ( saved !== result || notAsync ) {- dataPriv.set( this, type, false );- } else {- result = {};- }- if ( saved !== result ) {+ if ( result === oldResult && !forceSync ) {+ result = {};+ }+ if ( result !== oldResult ) {
// Cancel the outer synthetic event
event.stopImmediatePropagation();
event.preventDefault();
// Support: Chrome 86+
// 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
// (focus or blur), assume that the surrogate already propagated from triggering the
// native event and prevent that from happening again here.
// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
// bubbling surrogate propagates *after* the non-bubbling base), but that seems
// less bad than duplication.
} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
event.stopPropagation();
}
// If this is a native event triggered above, everything is now in order
// Fire an inner synthetic event with the original arguments
+ // for each queued event, and capture the last result- } else if ( saved.length ) {+ } else if ( length ) {- // ...and capture the result- dataPriv.set( this, type, {+ for ( ; i < length; i++ ) {+ result = ( {
value: jQuery.event.trigger(
// Support: IE <=9 - 11+
// Extend with the prototype to reset the above stopImmediatePropagation()
- jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),- saved.slice( 1 ),+ jQuery.extend( queue[ 0 ][ 0 ], jQuery.Event.prototype, { isInner: true } ),+ queue.shift().slice( 1 ),
this
)
} );
+ }
// Abort handling of the native event
event.stopImmediatePropagation();
}
}
} );
}
The concept should be sufficient to invoke handlers as expected, even with code like input1.trigger('focus', 0); input2.trigger('focus'); input1.trigger('focus', 1).
If there was an npm pre-release that would be easier :)
I can try manually later but it's been a while since we touched our v4-dev
branch. Let's hope things still work.
I understand, but my hope was to check before we release. 😉 Our release process is quite complex, so we don’t do a lot of pre-releases for minor updates.
FWIW, I did test manually using steps you provided above with jQuery swapped for the 3.x-git version and they passed. Is there anything else to test?
If you tried the following and works on BrowserStack then it should be fine:
git clone https://github.com/twbs/bootstrap.git -b v4-dev
npm i
# change the jquery version to the one you want to test
npm run js-test-cloud #assumes you have BrowserStack credentials set up
Activity
[-]v3.6.0 Internet Explorer breaks our test suite[/-][+]jQuery v3.6.0 breaks Bootstrap's test suite in IE[/+]mgol commentedon Mar 15, 2021
An isolated test case is basicaly:
In IE, focus stays on
input2.See versions with jQuery:
The 3.6.0 version fails in IE, the older ones succeed.
jQuery 3.4 should be similar to 3.5 as there weren't major changes in relevant areas.
I think this is the same bug that makes some tests fail in jQuery UI only with jQuery 3.6.0+:
Also related to #4856, although that issue manifests itself in all 3.4+ versions.
timmywil commentedon Mar 15, 2021
We'll address this in a patch. Related: dbcffb3
gibson042 commentedon Mar 15, 2021
@mgol The idea discussed in today's weekly meeting:
// Ensure the presence of an event listener that handles manually-triggered // synthetic events by interrupting progress until reinvoked in response to // *native* events that it fires directly, ensuring that state changes have // already occurred before other listeners are invoked. function leverageNative( el, type, expectSync ) { + var result = {}, queue = []; // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add if ( !expectSync ) { if ( dataPriv.get( el, type ) === undefined ) { jQuery.event.add( el, type, returnTrue ); } return; } // Register the controller as a special universal handler for all event namespaces dataPriv.set( el, type, false ); jQuery.event.add( el, type, { namespace: false, handler: function( event ) { - var notAsync, result, - saved = dataPriv.get( this, type ); + var forceSync, + i = 0, + length = queue.length, + oldResult = result; if ( ( event.isTrigger & 1 ) && this[ type ] ) { // Interrupt processing of the outer synthetic .trigger()ed event - // Saved data should be false in such cases, but might be a leftover capture object - // from an async native handler (gh-4350) - if ( !saved.length ) { + if ( !event.isInner ) { // Store arguments for use when handling the inner native event - // There will always be at least one argument (an event object), so this array - // will not be confused with a leftover capture object. - saved = slice.call( arguments ); - dataPriv.set( this, type, saved ); + queue.push( slice.call( arguments ) ); // Trigger the native event and capture its result // Support: IE <=9 - 11+ // focus() and blur() are asynchronous - notAsync = expectSync( this, type ); + forceSync = expectSync( this, type ); this[ type ](); - result = dataPriv.get( this, type ); - if ( saved !== result || notAsync ) { - dataPriv.set( this, type, false ); - } else { - result = {}; - } - if ( saved !== result ) { + if ( result === oldResult && !forceSync ) { + result = {}; + } + if ( result !== oldResult ) { // Cancel the outer synthetic event event.stopImmediatePropagation(); event.preventDefault(); // Support: Chrome 86+ // 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 // (focus or blur), assume that the surrogate already propagated from triggering the // native event and prevent that from happening again here. // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the // bubbling surrogate propagates *after* the non-bubbling base), but that seems // less bad than duplication. } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { event.stopPropagation(); } // If this is a native event triggered above, everything is now in order // Fire an inner synthetic event with the original arguments + // for each queued event, and capture the last result - } else if ( saved.length ) { + } else if ( length ) { - // ...and capture the result - dataPriv.set( this, type, { + for ( ; i < length; i++ ) { + result = ( { value: jQuery.event.trigger( // Support: IE <=9 - 11+ // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), - saved.slice( 1 ), + jQuery.extend( queue[ 0 ][ 0 ], jQuery.Event.prototype, { isInner: true } ), + queue.shift().slice( 1 ), this ) } ); + } // Abort handling of the native event event.stopImmediatePropagation(); } } } ); }The concept should be sufficient to invoke handlers as expected, even with code like
input1.trigger('focus', 0); input2.trigger('focus'); input1.trigger('focus', 1).mgol commentedon Mar 15, 2021
@gibson042 Thanks! Do you want to put that in a PR or just to provide a starting point for others to pick up?
jimmywarting commentedon May 9, 2021
mgol commentedon May 9, 2021
jimmywarting commentedon May 9, 2021
26 remaining items
Event: Simulate focus/blur in IE via focusin/focusout
Event: Simulate focus/blur in IE via focusin/focusout
Event: Simulate focus/blur in IE via focusin/focusout
Event: Simulate focus/blur in IE via focusin/focusout (3.x version)
mgol commentedon Mar 27, 2023
Fixed on
main(future4.x) via #5223 and on3.x-stablevia #5224.mgol commentedon Apr 4, 2023
@XhmikosR could you check whether https://releases.jquery.com/git/jquery-3.x-git.js works for you? This should be more or less what we'll release as jQuery 3.7.0.
XhmikosR commentedon Apr 4, 2023
mgol commentedon Apr 4, 2023
I understand, but my hope was to check before we release. 😉 Our release process is quite complex, so we don’t do a lot of pre-releases for minor updates.
FWIW, I did test manually using steps you provided above with jQuery swapped for the 3.x-git version and they passed. Is there anything else to test?
XhmikosR commentedon Apr 4, 2023
If you tried the following and works on BrowserStack then it should be fine:
mgol commentedon Apr 4, 2023
Yes, that’s what I did. It looks like we’re good then!
XhmikosR commentedon Apr 4, 2023
Perfect, thanks for fixing the issue!
mgol commentedon May 12, 2023
@XhmikosR jQuery 3.7.0 has been released, you should be able to update it in Bootstrap v4 now.
https://blog.jquery.com/2023/05/11/jquery-3-7-0-released-staying-in-order/
Use native focus events insted of jQuery focus events