Skip to content

Commit

Permalink
Event: Don't crash if an element is removed on blur
Browse files Browse the repository at this point in the history
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 private data undefined. We're reading a property from that
data so we need to guard against this.

Fixes gh-4417
Closes gh-4799

(cherry picked from commit 5c2d087)
  • Loading branch information
mgol committed Oct 19, 2020
1 parent 4c572a7 commit aaf9c55
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,13 @@ function leverageNative( el, type, expectSync ) {
// Cancel the outer synthetic event
event.stopImmediatePropagation();
event.preventDefault();
return result.value;

// 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
Expand Down
27 changes: 27 additions & 0 deletions test/unit/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -2625,6 +2625,33 @@ QUnit.test( "focusin on document & window", function( assert ) {
jQuery( document ).off( "focusout", increment );
} );

QUnit.test( "element removed during focusout (gh-4417)", function( assert ) {
assert.expect( 1 );

var button = jQuery( "<button>Click me</button>" );

button.appendTo( "#qunit-fixture" );

button.on( "click", function() {
button.trigger( "blur" );
assert.ok( true, "Removing the element didn't crash" );
} );

// Support: Chrome 86+
// In Chrome, if an element having a focusout handler is blurred by
// clicking outside of it, it invokes the handler synchronously. However,
// if the click happens programmatically, the invocation is asynchronous.
// As we have no way to simulate real user input in unit tests, simulate
// this behavior by calling `jQuery.cleanData` & removing the element using
// native APIs.
button[ 0 ].blur = function() {
jQuery.cleanData( [ this ] );
this.parentNode.removeChild( this );
};

button[ 0 ].click();
} );

testIframe(
"jQuery.ready promise",
"event/promiseReady.html",
Expand Down

0 comments on commit aaf9c55

Please sign in to comment.