Skip to content

Commit 14609f1

Browse files
Merge pull request #63 from canjs/event-delegation-handlers
Fixing event delegation
2 parents cbd3130 + 21c328e commit 14609f1

File tree

2 files changed

+73
-12
lines changed

2 files changed

+73
-12
lines changed

can-dom-events-test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,60 @@ require('./helpers/add-event-compat-test');
188188
require('./helpers/add-event-jquery-test');
189189
require('./helpers/add-jquery-events-test');
190190
require('./helpers/util-test');
191+
192+
unit.test('domEvents.addDelegateListener call inner-most handler first (#62)', function (assert) {
193+
var done = assert.async();
194+
var grandparent = document.createElement('div');
195+
var parent = document.createElement('p');
196+
var child = document.createElement('input');
197+
198+
grandparent.appendChild(parent);
199+
parent.appendChild(child);
200+
201+
var handlerCallCount = 0;
202+
203+
var paragraphClickHandler = function paragraphClickHandler() {
204+
domEvents.removeDelegateListener(grandparent, 'click', 'p', paragraphClickHandler);
205+
assert.equal(++handlerCallCount, 2, 'outer handler called second');
206+
};
207+
208+
var inputClickHandler = function inputClickHandler() {
209+
domEvents.removeDelegateListener(grandparent, 'click', 'input', inputClickHandler);
210+
assert.equal(++handlerCallCount, 1, 'inner handler called first');
211+
};
212+
213+
domEvents.addDelegateListener(grandparent, 'click', 'p', paragraphClickHandler);
214+
domEvents.addDelegateListener(grandparent, 'click', 'input', inputClickHandler);
215+
216+
domEvents.dispatch(child, 'click');
217+
done();
218+
});
219+
220+
['stopPropagation', 'stopImmediatePropagation'].forEach(function(stopMethod) {
221+
unit.test('domEvents.addDelegateListener should have a working ev.' + stopMethod + ' (#62)', function (assert) {
222+
var done = assert.async();
223+
var grandparent = document.createElement('div');
224+
var parent = document.createElement('p');
225+
var child = document.createElement('input');
226+
227+
grandparent.appendChild(parent);
228+
parent.appendChild(child);
229+
230+
var paragraphClickHandler = function paragraphClickHandler() {
231+
domEvents.removeDelegateListener(grandparent, 'click', 'p', paragraphClickHandler);
232+
assert.ok(false, 'ev.stopPropagation works');
233+
};
234+
235+
var inputClickHandler = function inputClickHandler(event) {
236+
event[stopMethod]();
237+
domEvents.removeDelegateListener(grandparent, 'click', 'input', inputClickHandler);
238+
assert.ok(true, 'inner-most click handler called first');
239+
};
240+
241+
domEvents.addDelegateListener(grandparent, 'click', 'p', paragraphClickHandler);
242+
domEvents.addDelegateListener(grandparent, 'click', 'input', inputClickHandler);
243+
244+
domEvents.dispatch(child, 'click');
245+
done();
246+
});
247+
});

helpers/-make-delegate-event-tree.js

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,30 @@ function makeDelegator (domEvents) {
1919
canReflect.assignSymbols( Delegator.prototype, {
2020
"can.setKeyValue": function(eventType, handlersBySelector){
2121
var handler = this.delegated[eventType] = function(ev){
22-
canReflect.each(handlersBySelector, function(handlers, selector){
23-
var cur = ev.target;
24-
do {
25-
// document does not implement `.matches` but documentElement does
26-
var el = cur === document ? document.documentElement : cur;
27-
var matches = el.matches || el.msMatchesSelector;
22+
var cur = ev.target;
23+
var propagate = true;
24+
ev.stopPropagation = ev.stopImmediatePropagation = function() {
25+
propagate = false;
26+
};
27+
do {
28+
// document does not implement `.matches` but documentElement does
29+
var el = cur === document ? document.documentElement : cur;
30+
var matches = el.matches || el.msMatchesSelector;
2831

32+
canReflect.each(handlersBySelector, function(handlers, selector){
2933
// Text and comment nodes may be included in mutation event targets
3034
// but will never match selectors (and do not implement matches)
3135
if (matches && matches.call(el, selector)) {
3236
handlers.forEach(function(handler){
3337
handler.call(el, ev);
3438
});
3539
}
36-
// since `el` points to `documentElement` when `cur` === document,
37-
// we need to continue using `cur` as the loop pointer, otherwhise
38-
// it will never end as documentElement.parentNode === document
39-
cur = cur.parentNode;
40-
} while (cur && cur !== ev.currentTarget);
41-
});
40+
});
41+
// since `el` points to `documentElement` when `cur` === document,
42+
// we need to continue using `cur` as the loop pointer, otherwhise
43+
// it will never end as documentElement.parentNode === document
44+
cur = cur.parentNode;
45+
} while ((cur && cur !== ev.currentTarget) && propagate);
4246
};
4347
this.events[eventType] = handlersBySelector;
4448
domEvents.addEventListener(this.element, eventType, handler, useCapture(eventType));

0 commit comments

Comments
 (0)