]> source.dussan.org Git - jquery.git/commitdiff
Event: Don't crash if an element is removed on blur
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Mon, 19 Oct 2020 19:17:51 +0000 (21:17 +0200)
committerGitHub <noreply@github.com>
Mon, 19 Oct 2020 19:17:51 +0000 (21:17 +0200)
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

src/event.js
test/unit/event.js

index b8c5e96fe3da2ab1ef2fbec82b32c59829b66408..4418fbbbf809eeab906938eec3ec77f5fae4e085 100644 (file)
@@ -558,7 +558,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
index 771283c2f63293c53c3866515ff5c842664bd849..90318f8fefb3c8ad7884374802aa2de7cda27157 100644 (file)
@@ -2630,6 +2630,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",