Skip to content

Commit c2caf48

Browse files
committed
fix(core events): Fixes for remove_event_listener.
- Fix removing all events on a given element. - Clean up the event registry after removing events.
1 parent 3dea198 commit c2caf48

File tree

2 files changed

+152
-18
lines changed

2 files changed

+152
-18
lines changed

src/core/events.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,19 @@ const remove_event_listener = (el, id) => {
5858
if (id) {
5959
// remove event listener with specific id
6060
const entry = el_events[id];
61-
entries = entry ? [entry] : [];
61+
entries = entry ? [[id, entry]] : [];
6262
} else {
6363
// remove all event listeners of element
6464
entries = Object.entries(el_events);
6565
}
6666
for (const entry of entries || []) {
67-
el.removeEventListener(entry[0], entry[1], entry[2]);
67+
el.removeEventListener(entry[1][0], entry[1][1], entry[1][2]);
68+
// Delete entry from event_listener_map
69+
delete event_listener_map[el][entry[0]];
70+
// Delete element from event_listener_map if no more events are registered.
71+
if (!Object.keys(event_listener_map[el]).length) {
72+
delete event_listener_map[el];
73+
}
6874
}
6975
};
7076

src/core/events.test.js

Lines changed: 144 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
import Base from "./base";
22
import { BasePattern } from "./basepattern";
3-
import events from "./events";
3+
import events, { event_listener_map } from "./events";
44
import utils from "./utils";
55

66
describe("core.events tests", () => {
77
describe("1 - add / remove event listener", () => {
8-
const _el = {
9-
event_list: [],
10-
addEventListener(event_type, cb) {
11-
this.event_list.push([event_type, cb]);
12-
},
13-
removeEventListener(event_type, cb) {
14-
const idx = this.event_list.indexOf([event_type, cb]);
15-
this.event_list.splice(idx, 1);
16-
},
17-
};
8+
afterEach(() => {
9+
// Clear event_listener_map after each test.
10+
for (const el in event_listener_map) {
11+
delete event_listener_map[el];
12+
}
13+
});
1814

1915
it("Registers events only once and unregisters events.", (done) => {
16+
// Mock a DOM element.
17+
const _el = {
18+
event_list: [],
19+
addEventListener(event_type, cb) {
20+
this.event_list.push([event_type, cb]);
21+
},
22+
removeEventListener(event_type, cb) {
23+
const idx = this.event_list.indexOf([event_type, cb]);
24+
this.event_list.splice(idx, 1);
25+
},
26+
};
27+
2028
const cb1 = () => {};
2129
const cb2 = () => {};
2230

@@ -50,8 +58,7 @@ describe("core.events tests", () => {
5058
done();
5159
});
5260

53-
it("Supports once-events and unregisters them from the event_listener_map", async () => {
54-
const event_listener_map = (await import("./events")).event_listener_map;
61+
it("Supports once-events and unregisters them from the event_listener_map", () => {
5562
const el = document.createElement("div");
5663

5764
// register the once-event handler
@@ -65,6 +72,127 @@ describe("core.events tests", () => {
6572
expect(event_listener_map[el].test_once_event).not.toBeDefined();
6673
});
6774

75+
it("Removes a specific event listener.", () => {
76+
const el = document.createElement("div");
77+
78+
let cnt = 0;
79+
80+
// register the once-event handler
81+
events.add_event_listener(el, "test", "test_event", () => cnt++);
82+
expect(event_listener_map[el].test_event).toBeDefined();
83+
84+
el.dispatchEvent(new Event("test"));
85+
expect(cnt).toBe(1);
86+
87+
el.dispatchEvent(new Event("test"));
88+
expect(cnt).toBe(2);
89+
90+
events.remove_event_listener(el, "test_event");
91+
92+
// Now the event listener should be removed.
93+
expect(event_listener_map[el]?.test_once_event).not.toBeDefined();
94+
// Even the element itself should be removed, if there are no more event listeners on it.
95+
expect(event_listener_map[el]).not.toBeDefined();
96+
97+
// counter should not increase anymore
98+
el.dispatchEvent(new Event("test"));
99+
expect(cnt).toBe(2);
100+
});
101+
102+
it("Remove single and all event listeners from an element, not touching others.", () => {
103+
const el1 = document.createElement("div");
104+
const el2 = document.createElement("span");
105+
106+
let cnt1 = 0;
107+
let cnt2 = 0;
108+
let cnt3 = 0;
109+
110+
const shared_cb = () => {
111+
cnt1++;
112+
};
113+
114+
// register the event handlers
115+
events.add_event_listener(el1, "test1", "test_event_1", shared_cb);
116+
events.add_event_listener(el1, "test2", "test_event_2", shared_cb);
117+
events.add_event_listener(el1, "test3", "test_event_3", () => cnt2++);
118+
events.add_event_listener(el2, "test4", "test_event_4", () => cnt3++);
119+
120+
expect(event_listener_map[el1].test_event_1).toBeDefined();
121+
expect(event_listener_map[el1].test_event_2).toBeDefined();
122+
expect(event_listener_map[el1].test_event_3).toBeDefined();
123+
expect(event_listener_map[el2].test_event_4).toBeDefined();
124+
125+
expect(Object.keys(event_listener_map).length).toBe(2);
126+
127+
el1.dispatchEvent(new Event("test1"));
128+
expect(cnt1).toBe(1);
129+
expect(cnt2).toBe(0);
130+
expect(cnt3).toBe(0);
131+
132+
el1.dispatchEvent(new Event("test1"));
133+
expect(cnt1).toBe(2);
134+
expect(cnt2).toBe(0);
135+
expect(cnt3).toBe(0);
136+
137+
el1.dispatchEvent(new Event("test2"));
138+
expect(cnt1).toBe(3);
139+
expect(cnt2).toBe(0);
140+
expect(cnt3).toBe(0);
141+
142+
el1.dispatchEvent(new Event("test3"));
143+
expect(cnt1).toBe(3);
144+
expect(cnt2).toBe(1);
145+
expect(cnt3).toBe(0);
146+
147+
el2.dispatchEvent(new Event("test4"));
148+
expect(cnt1).toBe(3);
149+
expect(cnt2).toBe(1);
150+
expect(cnt3).toBe(1);
151+
152+
// Remove only test_event_1
153+
events.remove_event_listener(el1, "test_event_1");
154+
expect(event_listener_map[el1].test_event_1).not.toBeDefined();
155+
expect(event_listener_map[el1].test_event_2).toBeDefined();
156+
expect(event_listener_map[el1].test_event_3).toBeDefined();
157+
expect(event_listener_map[el2].test_event_4).toBeDefined();
158+
expect(Object.keys(event_listener_map).length).toBe(2);
159+
160+
// Counter should not increase anymore on event "test1"
161+
el1.dispatchEvent(new Event("test1"));
162+
expect(cnt1).toBe(3);
163+
expect(cnt2).toBe(1);
164+
expect(cnt3).toBe(1);
165+
166+
// Rest should not be affected.
167+
el1.dispatchEvent(new Event("test2"));
168+
el1.dispatchEvent(new Event("test3"));
169+
el2.dispatchEvent(new Event("test4"));
170+
expect(cnt1).toBe(4);
171+
expect(cnt2).toBe(2);
172+
expect(cnt3).toBe(2);
173+
174+
// Remove all event handler on el1
175+
events.remove_event_listener(el1);
176+
expect(event_listener_map[el1]).not.toBeDefined();
177+
expect(Object.keys(event_listener_map).length).toBe(1);
178+
179+
// Counter should not increase anymore on el1
180+
el1.dispatchEvent(new Event("test1"));
181+
el1.dispatchEvent(new Event("test2"));
182+
el1.dispatchEvent(new Event("test3"));
183+
expect(cnt1).toBe(4);
184+
expect(cnt2).toBe(2);
185+
expect(cnt3).toBe(2);
186+
187+
// But el2 should still work.
188+
el2.dispatchEvent(new Event("test4"));
189+
expect(cnt1).toBe(4);
190+
expect(cnt2).toBe(2);
191+
expect(cnt3).toBe(3);
192+
});
193+
});
194+
195+
describe("2 - await pattern initialization", () => {
68196
it("Awaits an event to happen.", async () => {
69197
const el = document.createElement("div");
70198

@@ -164,7 +292,7 @@ describe("core.events tests", () => {
164292
});
165293
});
166294

167-
describe("2 - event factories", () => {
295+
describe("3 - event factories", () => {
168296
let catched;
169297
let outer;
170298
let inner;
@@ -287,7 +415,7 @@ describe("core.events tests", () => {
287415
});
288416
});
289417

290-
describe("3 - jQuery vs native", () => {
418+
describe("4 - jQuery vs native", () => {
291419
// These tests show an annoying difference between jQuery and native
292420
// JavaScript events. jQuery catches native JavaScript events, which is
293421
// good. But events triggered by jQuery are not compatibel with native
@@ -324,7 +452,7 @@ describe("core.events tests", () => {
324452
});
325453
});
326454

327-
describe("4 - Special DOM behavior", () => {
455+
describe("5 - Special DOM behavior", () => {
328456
afterEach(() => {
329457
document.body.innerHTML = "";
330458
});

0 commit comments

Comments
 (0)