Skip to content
This repository was archived by the owner on Mar 13, 2018. It is now read-only.

Commit 496f0a7

Browse files
committed
Dispatch native events at all times
If there are no listeners in a disconnected tree we add a temporary listener on the target which allows us to take over later in the process. Fixes #335
1 parent dbf6292 commit 496f0a7

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

src/wrappers/events.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,62 @@
656656
}
657657
},
658658
dispatchEvent: function(event) {
659-
dispatchEvent(event, this);
659+
// We want to use the native dispatchEvent because it triggers the default
660+
// actions (like checking a checkbox). However, if there are no listeners
661+
// in the composed tree then there are no events that will trigger and
662+
// listeners in the non composed tree that are part of the event path are
663+
// not notified.
664+
//
665+
// If we find out that there are no listeners in the composed tree we add
666+
// a temporary listener to the target which makes us get called back even
667+
// in that case.
668+
669+
var nativeEvent = unwrap(event);
670+
var eventType = nativeEvent.type;
671+
672+
// Allow dispatching the same event again. This is safe because if user
673+
// code calls this during an existing dispatch of the same event the
674+
// native dispatchEvent throws (that is required by the spec).
675+
handledEventsTable.set(nativeEvent, false);
676+
677+
// Force rendering since we prefer native dispatch and that works on the
678+
// composed tree.
679+
scope.renderAllPending();
680+
681+
var tempListener;
682+
if (!hasListenerInAncestors(this, eventType)) {
683+
tempListener = function() {};
684+
this.addEventListener(eventType, tempListener, true);
685+
}
686+
687+
try {
688+
return unwrap(this).dispatchEvent_(nativeEvent);
689+
} finally {
690+
if (tempListener)
691+
this.removeEventListener(eventType, tempListener, true);
692+
}
660693
}
661694
};
662695

696+
function hasListener(node, type) {
697+
var listeners = listenersTable.get(node);
698+
if (listeners) {
699+
for (var i = 0; i < listeners.length; i++) {
700+
if (!listeners[i].removed && listeners[i].type === type)
701+
return true;
702+
}
703+
}
704+
return false;
705+
}
706+
707+
function hasListenerInAncestors(target, type) {
708+
for (var node = unwrap(target); node; node = node.parentNode) {
709+
if (hasListener(wrap(node), type))
710+
return true;
711+
}
712+
return false;
713+
}
714+
663715
if (OriginalEventTarget)
664716
registerWrapper(OriginalEventTarget, EventTarget);
665717

test/js/events.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,4 +1361,28 @@ test('retarget order (multiple shadow roots)', function() {
13611361
assert.equal(data, 'b');
13621362
});
13631363

1364+
test('dispatch should trigger default actions', function() {
1365+
var div = document.createElement('div');
1366+
div.innerHTML = '<input type="checkbox">';
1367+
var checkbox = div.firstChild;
1368+
assert.isFalse(checkbox.checked);
1369+
checkbox.dispatchEvent(new MouseEvent('click'));
1370+
assert.isTrue(checkbox.checked);
1371+
});
1372+
1373+
test('dispatch should trigger default actions 2', function() {
1374+
var div = document.createElement('div');
1375+
div.innerHTML = '<input type="checkbox">';
1376+
var checkbox = div.firstChild;
1377+
var sr = div.createShadowRoot();
1378+
1379+
assert.isFalse(checkbox.checked);
1380+
checkbox.dispatchEvent(new MouseEvent('click'));
1381+
assert.isTrue(checkbox.checked);
1382+
1383+
div.offsetWidth;
1384+
checkbox.dispatchEvent(new MouseEvent('click'));
1385+
assert.isFalse(checkbox.checked);
1386+
});
1387+
13641388
});

0 commit comments

Comments
 (0)