Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 30354c5

Browse files
mbenfordmgol
authored andcommitted
fix(jqLite): fix event.stopImmediatePropagation() so it works as expected
jqLite doesn't override the default implementation of event.stopImmediatePropagation() and so it doesn't work as expected, i.e, it doesn't prevent the rest of the event handlers from being executed. Closes #4833
1 parent 19871d2 commit 30354c5

File tree

2 files changed

+110
-6
lines changed

2 files changed

+110
-6
lines changed

src/jqLite.js

+27-5
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,6 @@ forEach({
694694

695695
function createEventHandler(element, events) {
696696
var eventHandler = function (event, type) {
697-
698697
// jQuery specific api
699698
event.isDefaultPrevented = function() {
700699
return event.defaultPrevented;
@@ -705,13 +704,34 @@ function createEventHandler(element, events) {
705704

706705
if (!eventFnsLength) return;
707706

707+
if (isUndefined(event.immediatePropagationStopped)) {
708+
var originalStopImmediatePropagation = event.stopImmediatePropagation;
709+
event.stopImmediatePropagation = function() {
710+
event.immediatePropagationStopped = true;
711+
712+
if (event.stopPropagation) {
713+
event.stopPropagation();
714+
}
715+
716+
if (originalStopImmediatePropagation) {
717+
originalStopImmediatePropagation.call(event);
718+
}
719+
};
720+
}
721+
722+
event.isImmediatePropagationStopped = function() {
723+
return event.immediatePropagationStopped === true;
724+
};
725+
708726
// Copy event handlers in case event handlers array is modified during execution.
709727
if ((eventFnsLength > 1)) {
710728
eventFns = shallowCopy(eventFns);
711729
}
712730

713731
for (var i = 0; i < eventFnsLength; i++) {
714-
eventFns[i].call(element, event);
732+
if (!event.isImmediatePropagationStopped()) {
733+
eventFns[i].call(element, event);
734+
}
715735
}
716736
};
717737

@@ -912,11 +932,12 @@ forEach({
912932
var eventFns = events && events[eventName];
913933

914934
if (eventFns) {
915-
916935
// Create a dummy event to pass to the handlers
917936
dummyEvent = {
918937
preventDefault: function() { this.defaultPrevented = true; },
919938
isDefaultPrevented: function() { return this.defaultPrevented === true; },
939+
stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
940+
isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
920941
stopPropagation: noop,
921942
type: eventName,
922943
target: element
@@ -932,9 +953,10 @@ forEach({
932953
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
933954

934955
forEach(eventFnsCopy, function(fn) {
935-
fn.apply(element, handlerArgs);
956+
if (!dummyEvent.isImmediatePropagationStopped()) {
957+
fn.apply(element, handlerArgs);
958+
}
936959
});
937-
938960
}
939961
}
940962
}, function(fn, name){

test/jqLiteSpec.js

+83-1
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,58 @@ describe('jqLite', function() {
11021102
browserTrigger(a, 'click');
11031103
});
11041104

1105+
it('should stop triggering handlers when stopImmediatePropagation is called', function() {
1106+
var element = jqLite(a),
1107+
clickSpy1 = jasmine.createSpy('clickSpy1'),
1108+
clickSpy2 = jasmine.createSpy('clickSpy2').andCallFake(function(event) { event.stopImmediatePropagation(); }),
1109+
clickSpy3 = jasmine.createSpy('clickSpy3'),
1110+
clickSpy4 = jasmine.createSpy('clickSpy4');
1111+
1112+
element.on('click', clickSpy1);
1113+
element.on('click', clickSpy2);
1114+
element.on('click', clickSpy3);
1115+
element[0].addEventListener('click', clickSpy4);
1116+
1117+
browserTrigger(element, 'click');
1118+
1119+
expect(clickSpy1).toHaveBeenCalled();
1120+
expect(clickSpy2).toHaveBeenCalled();
1121+
expect(clickSpy3).not.toHaveBeenCalled();
1122+
expect(clickSpy4).not.toHaveBeenCalled();
1123+
});
1124+
1125+
it('should execute stopPropagation when stopImmediatePropagation is called', function() {
1126+
var element = jqLite(a),
1127+
clickSpy = jasmine.createSpy('clickSpy');
1128+
1129+
clickSpy.andCallFake(function(event) {
1130+
spyOn(event, 'stopPropagation');
1131+
event.stopImmediatePropagation();
1132+
expect(event.stopPropagation).toHaveBeenCalled();
1133+
});
1134+
1135+
element.on('click', clickSpy);
1136+
1137+
browserTrigger(element, 'click');
1138+
expect(clickSpy).toHaveBeenCalled();
1139+
});
1140+
1141+
it('should have event.isImmediatePropagationStopped method', function() {
1142+
var element = jqLite(a),
1143+
clickSpy = jasmine.createSpy('clickSpy');
1144+
1145+
clickSpy.andCallFake(function(event) {
1146+
expect(event.isImmediatePropagationStopped()).toBe(false);
1147+
event.stopImmediatePropagation();
1148+
expect(event.isImmediatePropagationStopped()).toBe(true);
1149+
});
1150+
1151+
element.on('click', clickSpy);
1152+
1153+
browserTrigger(element, 'click');
1154+
expect(clickSpy).toHaveBeenCalled();
1155+
});
1156+
11051157
describe('mouseenter-mouseleave', function() {
11061158
var root, parent, sibling, child, log;
11071159

@@ -1784,7 +1836,6 @@ describe('jqLite', function() {
17841836
expect(event.isDefaultPrevented()).toBe(true);
17851837
});
17861838

1787-
17881839
it('should support handlers that deregister themselves', function() {
17891840
var element = jqLite('<a>poke</a>'),
17901841
clickSpy = jasmine.createSpy('click'),
@@ -1821,6 +1872,37 @@ describe('jqLite', function() {
18211872
expect(actualEvent.target).toEqual(element[0]);
18221873
expect(actualEvent.type).toEqual('click');
18231874
});
1875+
1876+
it('should stop triggering handlers when stopImmediatePropagation is called', function () {
1877+
var element = jqLite(a),
1878+
clickSpy1 = jasmine.createSpy('clickSpy1'),
1879+
clickSpy2 = jasmine.createSpy('clickSpy2').andCallFake(function(event) { event.stopImmediatePropagation(); }),
1880+
clickSpy3 = jasmine.createSpy('clickSpy3');
1881+
1882+
element.on('click', clickSpy1);
1883+
element.on('click', clickSpy2);
1884+
element.on('click', clickSpy3);
1885+
1886+
element.triggerHandler('click');
1887+
1888+
expect(clickSpy1).toHaveBeenCalled();
1889+
expect(clickSpy2).toHaveBeenCalled();
1890+
expect(clickSpy3).not.toHaveBeenCalled();
1891+
});
1892+
1893+
it('should have event.isImmediatePropagationStopped method', function() {
1894+
var element = jqLite(a),
1895+
clickSpy = jasmine.createSpy('clickSpy'),
1896+
event;
1897+
1898+
element.on('click', clickSpy);
1899+
element.triggerHandler('click');
1900+
event = clickSpy.mostRecentCall.args[0];
1901+
1902+
expect(event.isImmediatePropagationStopped()).toBe(false);
1903+
event.stopImmediatePropagation();
1904+
expect(event.isImmediatePropagationStopped()).toBe(true);
1905+
});
18241906
});
18251907

18261908

0 commit comments

Comments
 (0)