Skip to content

Commit aaf9c55

Browse files
committed
Event: Don't crash if an element is removed on blur
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)
1 parent 4c572a7 commit aaf9c55

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

src/event.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,13 @@ function leverageNative( el, type, expectSync ) {
581581
// Cancel the outer synthetic event
582582
event.stopImmediatePropagation();
583583
event.preventDefault();
584-
return result.value;
584+
585+
// Support: Chrome 86+
586+
// In Chrome, if an element having a focusout handler is blurred by
587+
// clicking outside of it, it invokes the handler synchronously. If
588+
// that handler calls `.remove()` on the element, the data is cleared,
589+
// leaving `result` undefined. We need to guard against this.
590+
return result && result.value;
585591
}
586592

587593
// If this is an inner synthetic event for an event with a bubbling surrogate

test/unit/event.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2625,6 +2625,33 @@ QUnit.test( "focusin on document & window", function( assert ) {
26252625
jQuery( document ).off( "focusout", increment );
26262626
} );
26272627

2628+
QUnit.test( "element removed during focusout (gh-4417)", function( assert ) {
2629+
assert.expect( 1 );
2630+
2631+
var button = jQuery( "<button>Click me</button>" );
2632+
2633+
button.appendTo( "#qunit-fixture" );
2634+
2635+
button.on( "click", function() {
2636+
button.trigger( "blur" );
2637+
assert.ok( true, "Removing the element didn't crash" );
2638+
} );
2639+
2640+
// Support: Chrome 86+
2641+
// In Chrome, if an element having a focusout handler is blurred by
2642+
// clicking outside of it, it invokes the handler synchronously. However,
2643+
// if the click happens programmatically, the invocation is asynchronous.
2644+
// As we have no way to simulate real user input in unit tests, simulate
2645+
// this behavior by calling `jQuery.cleanData` & removing the element using
2646+
// native APIs.
2647+
button[ 0 ].blur = function() {
2648+
jQuery.cleanData( [ this ] );
2649+
this.parentNode.removeChild( this );
2650+
};
2651+
2652+
button[ 0 ].click();
2653+
} );
2654+
26282655
testIframe(
26292656
"jQuery.ready promise",
26302657
"event/promiseReady.html",

0 commit comments

Comments
 (0)