Skip to content

Commit 96e9768

Browse files
committed
handle event listener registration before mount; handle unregister
1 parent 149c100 commit 96e9768

File tree

2 files changed

+44
-3
lines changed

2 files changed

+44
-3
lines changed

src/runtime/internal/Component.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ if (typeof HTMLElement === 'function') {
152152
private $$data = {};
153153
private $$reflecting = false;
154154
private $$props_definition: Record<string, CustomElementPropDefinition> = {};
155+
private $$listeners: Record<string, Function[]> = {};
156+
private $$listener_unsubscribe_fns = new Map<Function, Function>();
155157

156158
constructor(
157159
private $$componentCtor: ComponentType,
@@ -165,10 +167,26 @@ if (typeof HTMLElement === 'function') {
165167
// We can't determine upfront if the event is a custom event or not, so we have to
166168
// listen to both. If someone uses a custom event with the same name as a regular
167169
// browser event, this fires twice - we can't avoid that.
168-
this.$$component!.$on(type, listener);
170+
this.$$listeners[type] = this.$$listeners[type] || [];
171+
this.$$listeners[type].push(listener);
172+
if (this.$$component) {
173+
const unsub = this.$$component!.$on(type, listener);
174+
this.$$listener_unsubscribe_fns.set(listener, unsub);
175+
}
169176
super.addEventListener(type, listener, options);
170177
}
171178

179+
removeEventListener(type: string, listener: any, options?: any): void {
180+
super.removeEventListener(type, listener, options);
181+
if (this.$$component) {
182+
const unsub = this.$$listener_unsubscribe_fns.get(listener);
183+
if (unsub) {
184+
unsub();
185+
this.$$listener_unsubscribe_fns.delete(listener);
186+
}
187+
}
188+
}
189+
172190
async connectedCallback() {
173191
this.$$connected = true;
174192
if (!this.$$component) {
@@ -228,6 +246,14 @@ if (typeof HTMLElement === 'function') {
228246
}
229247
}
230248
});
249+
250+
for (const type in this.$$listeners) {
251+
for (const listener of this.$$listeners[type]) {
252+
const unsub = this.$$component!.$on(type, listener);
253+
this.$$listener_unsubscribe_fns.set(listener, unsub);
254+
}
255+
}
256+
this.$$listeners = {};
231257
}
232258
}
233259

test/custom-elements/samples/events/test.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@ import './main.svelte';
44

55
export default async function (target) {
66
target.innerHTML = '<custom-element></custom-element>';
7-
await tick();
87
const el = target.querySelector('custom-element');
98

109
const events = [];
10+
const custom_before = () => {
11+
events.push('before');
12+
};
13+
const click_before = () => {
14+
events.push('click_before');
15+
};
16+
el.addEventListener('custom', custom_before);
17+
el.addEventListener('click', click_before);
18+
19+
await tick();
20+
1121
el.addEventListener('custom', e => {
1222
events.push(e.detail);
1323
});
@@ -16,5 +26,10 @@ export default async function (target) {
1626
});
1727

1828
el.shadowRoot.querySelector('button').click();
19-
assert.deepEqual(events, ['foo', 'click']);
29+
assert.deepEqual(events, ['before', 'foo', 'click_before', 'click']);
30+
31+
el.removeEventListener('custom', custom_before);
32+
el.removeEventListener('click', click_before);
33+
el.shadowRoot.querySelector('button').click();
34+
assert.deepEqual(events, ['before', 'foo', 'click_before', 'click', 'foo', 'click']);
2035
}

0 commit comments

Comments
 (0)