diff --git a/src/event.js b/src/event.js
index 5004b8ecf..6a592fdd0 100644
--- a/src/event.js
+++ b/src/event.js
@@ -648,17 +648,22 @@ jQuery.event = {
}
},
+ // Piggyback on a donor event to simulate a different one
simulate: function( type, elem, event, bubble ) {
- // Piggyback on a donor event to simulate a different one.
- // Fake originalEvent to avoid donor's stopPropagation, but if the
- // simulated event prevents default then we do the same on the donor.
var e = jQuery.extend(
new jQuery.Event(),
event,
{
type: type,
- isSimulated: true,
- originalEvent: {}
+ isSimulated: true
+ // Previously, `originalEvent: {}` was set here, so stopPropagation call
+ // would not be triggered on donor event, since in our own
+ // jQuery.event.stopPropagation function we had a check for existence of
+ // originalEvent.stopPropagation method, so, consequently it would be a noop.
+ //
+ // Guard for simulated events was moved to jQuery.event.stopPropagation function
+ // since `originalEvent` should point to the original event for the
+ // constancy with other events and for more focused logic
}
);
if ( bubble ) {
@@ -763,7 +768,8 @@ jQuery.Event.prototype = {
var e = this.originalEvent;
this.isPropagationStopped = returnTrue;
- if ( !e ) {
+
+ if ( !e || this.isSimulated ) {
return;
}
// If stopPropagation exists, run it on the original event
diff --git a/test/unit/event.js b/test/unit/event.js
index 0476b8e92..785698a47 100644
--- a/test/unit/event.js
+++ b/test/unit/event.js
@@ -2686,6 +2686,135 @@ test( "Inline event result is returned (#13993)", function() {
equal( result, 42, "inline handler returned value" );
});
+test( "preventDefault() on focusin does not throw exception", function( assert ) {
+ expect( 1 );
+
+ var done = assert.async(),
+ input = jQuery( "" ).appendTo( "#form" );
+
+ input.on( "focusin", function( event ) {
+ var exceptionCaught;
+
+ try {
+ event.preventDefault();
+ } catch ( theException ) {
+ exceptionCaught = theException;
+ }
+
+ assert.strictEqual( exceptionCaught, undefined,
+ "Preventing default on focusin throws no exception" );
+
+ done();
+ } ).trigger( "focus" );
+} );
+
+test( "Donor event interference", function( assert ) {
+ assert.expect( 10 );
+
+ var html = "
" +
+ "" +
+ "
";
+
+ jQuery( "#qunit-fixture" ).append( html );
+
+ jQuery( "#donor-outer" ).on( "click", function( event ) {
+ assert.ok( true, "click bubbled to outer div" );
+ assert.equal( typeof event.originalEvent, "object", "make sure originalEvent exist" );
+ assert.equal( event.type, "click", "make sure event type is correct" );
+ } );
+ jQuery( "#donor-input" ).on( "click", function( event ) {
+ assert.ok( true, "got a click event from the input" );
+ assert.ok( !event.isPropagationStopped(), "propagation says it's not stopped" );
+ assert.equal( event.type, "click", "make sure event type is correct" );
+ assert.equal( typeof event.originalEvent, "object", "make sure originalEvent exist" );
+ } );
+ jQuery( "#donor-input" ).on( "change", function( event ) {
+ assert.equal( typeof event.originalEvent, "object", "make sure originalEvent exist" );
+ assert.equal( event.type, "change", "make sure event type is correct" );
+ assert.ok( true, "got a change event from the input" );
+ event.stopPropagation();
+ } );
+
+ jQuery( "#donor-input" )[ 0 ].click();
+} );
+
+test( "originalEvent property for IE8", function( assert ) {
+ if ( !(/msie 8\.0/i.test( window.navigator.userAgent )) ) {
+ assert.expect( 1 );
+ assert.ok( true, "Assertions should run only in IE" );
+ return;
+ }
+
+ assert.expect( 12 );
+
+ var html = "
" +
+ "" +
+ "
";
+
+ jQuery( "#qunit-fixture" ).append( html );
+
+ jQuery( "#donor-outer" ).on( "change", function( event ) {
+ assert.ok( true, "click bubbled to outer div" );
+ assert.equal( event.originalEvent.type, "click", "make sure simulated event is a click" );
+ assert.equal( event.type, "change", "make sure event type is correct" );
+ } );
+
+ jQuery( "#donor-outer" ).on( "click", function( event ) {
+ assert.ok( true, "click bubbled to outer div" );
+ assert.equal( event.originalEvent.type, "click", "make sure originalEvent exist" );
+ } );
+ jQuery( "#donor-input" ).on( "click", function( event ) {
+ assert.ok( true, "got a click event from the input" );
+ assert.ok( !event.isPropagationStopped(), "propagation says it's not stopped" );
+ assert.equal( event.originalEvent.type, "click", "make sure originalEvent exist" );
+ assert.equal( event.type, "click", "make sure event type is correct" );
+ } );
+ jQuery( "#donor-input" ).on( "change", function( event ) {
+ assert.equal( event.originalEvent.type, "click", "make sure originalEvent exist" );
+ assert.equal( event.type, "change", "make sure event type is correct" );
+ assert.ok( true, "got a change event from the input" );
+ } );
+
+ jQuery( "#donor-input" )[ 0 ].click();
+} );
+
+test( "originalEvent property for Chrome, Safari and FF of simulated event", function( assert ) {
+ var userAgent = window.navigator.userAgent;
+
+ if ( !(/chrome/i.test( userAgent ) ||
+ /firefox/i.test( userAgent ) ||
+ /safari/i.test( userAgent ) ) ) {
+ assert.expect( 1 );
+ assert.ok( true, "Assertions should run only in Chrome, Safari and FF" );
+ return;
+ }
+
+ assert.expect( 4 );
+
+ var html = "
" +
+ "" +
+ "
";
+
+ jQuery( "#qunit-fixture" ).append( html );
+
+ jQuery( "#donor-outer" ).on( "focusin", function( event ) {
+ assert.ok( true, "focusin bubbled to outer div" );
+ assert.equal( event.originalEvent.type, "focus",
+ "make sure originalEvent type is correct" );
+ assert.equal( event.type, "focusin", "make sure type is correct" );
+ } );
+ jQuery( "#donor-input" ).on( "focus", function() {
+ assert.ok( true, "got a focus event from the input" );
+ } );
+ jQuery( "#donor-input" ).trigger( "focus" );
+} );
+
// This tests are unreliable in Firefox
if ( !(/firefox/i.test( window.navigator.userAgent )) ) {
test( "Check order of focusin/focusout events", 2, function() {