Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -545,8 +545,8 @@ function leverageNative( el, type, isSetup ) {
}

// 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.
// (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.
Expand All @@ -565,8 +565,16 @@ function leverageNative( el, type, isSetup ) {
this
) );

// Abort handling of the native event
event.stopImmediatePropagation();
// Abort handling of the native event by all jQuery handlers while allowing
// native handlers on the same element to run. On target, this is achieved
// by stopping immediate propagation just on the jQuery event. However,
// the native event is re-wrapped by a jQuery one on each level of the
// propagation so the only way to stop it for jQuery is to stop it for
// everyone via native `stopPropagation()`. This is not a problem for
// focus/blur which don't bubble, but it does also stop click on checkboxes
// and radios. We accept this limitation.
event.stopPropagation();
event.isImmediatePropagationStopped = returnTrue;
}
}
} );
Expand Down
63 changes: 61 additions & 2 deletions test/unit/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -3423,6 +3423,36 @@ QUnit.test( "trigger(focus) works after focusing when hidden (gh-4950)", functio
assert.equal( document.activeElement, input[ 0 ], "input has focus" );
} );

QUnit.test( "trigger(focus) fires native & jQuery handlers (gh-5015)", function( assert ) {
assert.expect( 3 );

var input = jQuery( "<input />" ),

// Support: IE 9 - 11+
// focus is async in IE; we now emulate it via sync focusin in jQuery
// but this test also attaches native handlers.
done = assert.async( 3 );

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

input[ 0 ].addEventListener( "focus", function() {
assert.ok( true, "1st native handler fired" );
done();
} );

input.on( "focus", function() {
assert.ok( true, "jQuery handler fired" );
done();
} );

input[ 0 ].addEventListener( "focus", function() {
assert.ok( true, "2nd native handler fired" );
done();
} );

input.trigger( "focus" );
} );

// TODO replace with an adaptation of
// https://github.com/jquery/jquery/pull/1367/files#diff-a215316abbaabdf71857809e8673ea28R2464
( function() {
Expand All @@ -3431,10 +3461,13 @@ QUnit.test( "trigger(focus) works after focusing when hidden (gh-4950)", functio
checkbox: "<input type='checkbox'>",
radio: "<input type='radio'>"
},
makeTestFor3751
function( type, html ) {
makeTestForGh3751( type, html );
makeTestForGh5015( type, html );
}
);

function makeTestFor3751( type, html ) {
function makeTestForGh3751( type, html ) {
var testName = "native-backed namespaced clicks are handled correctly (gh-3751) - " + type;
QUnit.test( testName, function( assert ) {
assert.expect( 2 );
Expand All @@ -3461,4 +3494,30 @@ QUnit.test( "trigger(focus) works after focusing when hidden (gh-4950)", functio
target.trigger( "click.fired" );
} );
}

function makeTestForGh5015( type, html ) {
var testName = "trigger(click) fires native & jQuery handlers (gh-5015) - " + type;
QUnit.test( testName, function( assert ) {
assert.expect( 3 );

var parent = supportjQuery( "<div class='parent'>" + html + "</div>" ),
input = jQuery( parent[ 0 ].firstChild );

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

input[ 0 ].addEventListener( "click", function() {
assert.ok( true, "1st native handler fired" );
} );

input.on( "click", function() {
assert.ok( true, "jQuery handler fired" );
} );

input[ 0 ].addEventListener( "click", function() {
assert.ok( true, "2nd native handler fired" );
} );

input.trigger( "click" );
} );
}
} )();