Skip to content

Commit 661b74c

Browse files
committed
breaking(core events): Move add_event_listener and remove_event_listener to core.events.
Move add_event_listener and remove_event_listener from core.dom to core.events. Provide backwards compatibility imports in core.dom. Those imports will be removed in an upcoming version.
1 parent 951084f commit 661b74c

File tree

9 files changed

+172
-157
lines changed

9 files changed

+172
-157
lines changed

src/core/dom.js

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* Utilities for DOM traversal or navigation */
2+
import events from "./events";
23

34
const DATA_STYLE_DISPLAY = "__patternslib__style__display";
45

@@ -137,64 +138,6 @@ const create_from_string = (string) => {
137138
return div.firstChild;
138139
};
139140

140-
// Event listener registration for easy-to-remove event listeners.
141-
// once Safari supports the ``signal`` option for addEventListener we can abort
142-
// event handlers by calling AbortController.abort().
143-
const event_listener_map = {};
144-
145-
/**
146-
* Add an event listener to a DOM element under a unique id.
147-
* If a event is registered under the same id for the same element, the old handler is removed first.
148-
*
149-
* @param {DOM Node} el - The element to register the event for.
150-
* @param {string} event_type - The event type to listen for.
151-
* @param {string} id - A unique id under which the event is registered.
152-
* @param {function} cb - The event handler / callback function.
153-
* @param {Object} opts - Options for the addEventListener API.
154-
*
155-
*/
156-
const add_event_listener = (el, event_type, id, cb, opts = {}) => {
157-
if (!el?.addEventListener) {
158-
return; // nothing to do.
159-
}
160-
remove_event_listener(el, id); // do not register one listener twice.
161-
162-
if (!event_listener_map[el]) {
163-
event_listener_map[el] = {};
164-
}
165-
event_listener_map[el][id] = [event_type, cb, opts.capture ? opts : undefined]; // prettier-ignore
166-
el.addEventListener(event_type, cb, opts);
167-
};
168-
169-
/**
170-
* Remove an event listener from a DOM element under a unique id.
171-
*
172-
* @param {DOM Node} el - The element to register the event for.
173-
* @param {string} id - A unique id under which the event is registered.
174-
*
175-
*/
176-
const remove_event_listener = (el, id) => {
177-
if (!el?.removeEventListener) {
178-
return; // nothing to do.
179-
}
180-
const el_events = event_listener_map[el];
181-
if (!el_events) {
182-
return;
183-
}
184-
let entries;
185-
if (id) {
186-
// remove event listener with specific id
187-
const entry = el_events[id];
188-
entries = entry ? [entry] : [];
189-
} else {
190-
// remove all event listeners of element
191-
entries = Object.entries(el_events);
192-
}
193-
for (const entry of entries || []) {
194-
el.removeEventListener(entry[0], entry[1], entry[2]);
195-
}
196-
};
197-
198141
const dom = {
199142
toNodeArray: toNodeArray,
200143
querySelectorAllAndMe: querySelectorAllAndMe,
@@ -207,8 +150,8 @@ const dom = {
207150
acquire_attribute: acquire_attribute,
208151
is_visible: is_visible,
209152
create_from_string: create_from_string,
210-
add_event_listener: add_event_listener,
211-
remove_event_listener: remove_event_listener,
153+
add_event_listener: events.add_event_listener, // BBB export. TODO: Remove in an upcoming version.
154+
remove_event_listener: events.remove_event_listener, // BBB export. TODO: Remove in an upcoming version.
212155
};
213156

214157
export default dom;

src/core/dom.test.js

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -458,51 +458,4 @@ describe("core.dom tests", () => {
458458
done();
459459
});
460460
});
461-
462-
describe("add / remove event listener", () => {
463-
const _el = {
464-
event_list: [],
465-
addEventListener(event_type, cb) {
466-
this.event_list.push([event_type, cb]);
467-
},
468-
removeEventListener(event_type, cb) {
469-
const idx = this.event_list.indexOf([event_type, cb]);
470-
this.event_list.splice(idx, 1);
471-
},
472-
};
473-
474-
it("Registers events only once and unregisters events.", (done) => {
475-
const cb1 = () => {};
476-
const cb2 = () => {};
477-
478-
// register one event handler
479-
dom.add_event_listener(_el, "click", "test_click", cb1);
480-
expect(_el.event_list.length).toBe(1);
481-
expect(_el.event_list[0][1]).toBe(cb1);
482-
483-
// register another event hander under the same id
484-
dom.add_event_listener(_el, "click", "test_click", cb2);
485-
expect(_el.event_list.length).toBe(1);
486-
expect(_el.event_list[0][1]).toBe(cb2);
487-
488-
// register two more event handlers with unique ids
489-
dom.add_event_listener(_el, "click", "test_click_2", () => {});
490-
dom.add_event_listener(_el, "click", "test_click_3", () => {});
491-
expect(_el.event_list.length).toBe(3);
492-
493-
// remove one specific event handler
494-
dom.remove_event_listener(_el, "test_click_2");
495-
expect(_el.event_list.length).toBe(2);
496-
497-
// try to remove an unregistered event handler
498-
dom.remove_event_listener(_el, "test_click_4");
499-
expect(_el.event_list.length).toBe(2);
500-
501-
// remove all registered event handlers on that element
502-
dom.remove_event_listener(_el);
503-
expect(_el.event_list.length).toBe(0);
504-
505-
done();
506-
});
507-
});
508461
});

src/core/events.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,63 @@
1+
// Event related methods and event factories
2+
3+
// Event listener registration for easy-to-remove event listeners.
4+
// once Safari supports the ``signal`` option for addEventListener we can abort
5+
// event handlers by calling AbortController.abort().
6+
const event_listener_map = {};
7+
8+
/**
9+
* Add an event listener to a DOM element under a unique id.
10+
* If a event is registered under the same id for the same element, the old handler is removed first.
11+
*
12+
* @param {DOM Node} el - The element to register the event for.
13+
* @param {string} event_type - The event type to listen for.
14+
* @param {string} id - A unique id under which the event is registered.
15+
* @param {function} cb - The event handler / callback function.
16+
* @param {Object} opts - Options for the addEventListener API.
17+
*
18+
*/
19+
const add_event_listener = (el, event_type, id, cb, opts = {}) => {
20+
if (!el?.addEventListener) {
21+
return; // nothing to do.
22+
}
23+
remove_event_listener(el, id); // do not register one listener twice.
24+
25+
if (!event_listener_map[el]) {
26+
event_listener_map[el] = {};
27+
}
28+
event_listener_map[el][id] = [event_type, cb, opts.capture ? opts : undefined]; // prettier-ignore
29+
el.addEventListener(event_type, cb, opts);
30+
};
31+
32+
/**
33+
* Remove an event listener from a DOM element under a unique id.
34+
*
35+
* @param {DOM Node} el - The element to register the event for.
36+
* @param {string} id - A unique id under which the event is registered.
37+
*
38+
*/
39+
const remove_event_listener = (el, id) => {
40+
if (!el?.removeEventListener) {
41+
return; // nothing to do.
42+
}
43+
const el_events = event_listener_map[el];
44+
if (!el_events) {
45+
return;
46+
}
47+
let entries;
48+
if (id) {
49+
// remove event listener with specific id
50+
const entry = el_events[id];
51+
entries = entry ? [entry] : [];
52+
} else {
53+
// remove all event listeners of element
54+
entries = Object.entries(el_events);
55+
}
56+
for (const entry of entries || []) {
57+
el.removeEventListener(entry[0], entry[1], entry[2]);
58+
}
59+
};
60+
161
const change_event = () => {
262
return new Event("change", {
363
bubbles: true,
@@ -20,6 +80,8 @@ const submit_event = () => {
2080
};
2181

2282
export default {
83+
add_event_listener: add_event_listener,
84+
remove_event_listener: remove_event_listener,
2385
change_event: change_event,
2486
input_event: input_event,
2587
submit_event: submit_event,

src/core/events.test.js

Lines changed: 80 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,95 @@ import events from "./events";
22
import utils from "./utils";
33

44
describe("core.events tests", () => {
5-
let catched;
6-
let outer;
7-
let inner;
8-
9-
beforeEach(() => {
10-
catched = null;
11-
const el = document.createElement("div");
12-
el.innerHTML = `
5+
describe("add / remove event listener", () => {
6+
const _el = {
7+
event_list: [],
8+
addEventListener(event_type, cb) {
9+
this.event_list.push([event_type, cb]);
10+
},
11+
removeEventListener(event_type, cb) {
12+
const idx = this.event_list.indexOf([event_type, cb]);
13+
this.event_list.splice(idx, 1);
14+
},
15+
};
16+
17+
it("Registers events only once and unregisters events.", (done) => {
18+
const cb1 = () => {};
19+
const cb2 = () => {};
20+
21+
// register one event handler
22+
events.add_event_listener(_el, "click", "test_click", cb1);
23+
expect(_el.event_list.length).toBe(1);
24+
expect(_el.event_list[0][1]).toBe(cb1);
25+
26+
// register another event hander under the same id
27+
events.add_event_listener(_el, "click", "test_click", cb2);
28+
expect(_el.event_list.length).toBe(1);
29+
expect(_el.event_list[0][1]).toBe(cb2);
30+
31+
// register two more event handlers with unique ids
32+
events.add_event_listener(_el, "click", "test_click_2", () => {});
33+
events.add_event_listener(_el, "click", "test_click_3", () => {});
34+
expect(_el.event_list.length).toBe(3);
35+
36+
// remove one specific event handler
37+
events.remove_event_listener(_el, "test_click_2");
38+
expect(_el.event_list.length).toBe(2);
39+
40+
// try to remove an unregistered event handler
41+
events.remove_event_listener(_el, "test_click_4");
42+
expect(_el.event_list.length).toBe(2);
43+
44+
// remove all registered event handlers on that element
45+
events.remove_event_listener(_el);
46+
expect(_el.event_list.length).toBe(0);
47+
48+
done();
49+
});
50+
});
51+
52+
describe("event factories", () => {
53+
let catched;
54+
let outer;
55+
let inner;
56+
57+
beforeEach(() => {
58+
catched = null;
59+
const el = document.createElement("div");
60+
el.innerHTML = `
1361
<div id="outer">
1462
<div id="inner"></div>
1563
</div>
1664
`;
17-
outer = el.querySelector("#outer");
18-
inner = el.querySelector("#inner");
19-
});
65+
outer = el.querySelector("#outer");
66+
inner = el.querySelector("#inner");
67+
});
2068

21-
it("change event", async () => {
22-
outer.addEventListener("change", () => {
23-
catched = "outer";
69+
it("change event", async () => {
70+
outer.addEventListener("change", () => {
71+
catched = "outer";
72+
});
73+
inner.dispatchEvent(events.change_event());
74+
await utils.timeout(1);
75+
expect(catched).toBe("outer");
2476
});
25-
inner.dispatchEvent(events.change_event());
26-
await utils.timeout(1);
27-
expect(catched).toBe("outer");
28-
});
2977

30-
it("input event", async () => {
31-
outer.addEventListener("input", () => {
32-
catched = "outer";
78+
it("input event", async () => {
79+
outer.addEventListener("input", () => {
80+
catched = "outer";
81+
});
82+
inner.dispatchEvent(events.input_event());
83+
await utils.timeout(1);
84+
expect(catched).toBe("outer");
3385
});
34-
inner.dispatchEvent(events.input_event());
35-
await utils.timeout(1);
36-
expect(catched).toBe("outer");
37-
});
3886

39-
it("submit event", async () => {
40-
outer.addEventListener("submit", () => {
41-
catched = "outer";
87+
it("submit event", async () => {
88+
outer.addEventListener("submit", () => {
89+
catched = "outer";
90+
});
91+
inner.dispatchEvent(events.submit_event());
92+
await utils.timeout(1);
93+
expect(catched).toBe("outer");
4294
});
43-
inner.dispatchEvent(events.submit_event());
44-
await utils.timeout(1);
45-
expect(catched).toBe("outer");
4695
});
4796
});

src/pat/auto-suggest/auto-suggest.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export default Base.extend({
111111
});
112112

113113
// Clear values on reset.
114-
dom.add_event_listener(
114+
events.add_event_listener(
115115
this.el.closest("form"),
116116
"reset",
117117
"pat-auto-suggest--reset",

src/pat/date-picker/date-picker.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import "regenerator-runtime/runtime"; // needed for ``await`` support
33
import $ from "jquery";
44
import Base from "../../core/base";
55
import Parser from "../../core/parser";
6-
import utils from "../../core/utils";
76
import dom from "../../core/dom";
7+
import events from "../../core/events";
8+
import utils from "../../core/utils";
89

910
export const parser = new Parser("date-picker");
1011
parser.addArgument("behavior", "styled", ["native", "styled"]);
@@ -82,9 +83,14 @@ export default Base.extend({
8283
// click on display_el on Chrome.
8384
const label = display_el.closest("label");
8485
if (label) {
85-
dom.add_event_listener(label, "click", "pat-date-picker--label", (e) => {
86-
e.preventDefault();
87-
});
86+
events.add_event_listener(
87+
label,
88+
"click",
89+
"pat-date-picker--label",
90+
(e) => {
91+
e.preventDefault();
92+
}
93+
);
8894
}
8995

9096
let display_el_pat;

src/pat/gallery/gallery.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import "regenerator-runtime/runtime"; // needed for ``await`` support
33
import Base from "../../core/base";
44
import Parser from "../../core/parser";
55
import dom from "../../core/dom";
6+
import events from "../../core/events";
67
import logging from "../../core/logging";
78
import utils from "../../core/utils";
89

@@ -71,7 +72,7 @@ export default Base.extend({
7172
this.options.itemSelector
7273
);
7374
this.images = [...image_wrapper_els].map((it) => {
74-
dom.add_event_listener(
75+
events.add_event_listener(
7576
it,
7677
"click",
7778
"pat-gallery--image_handler",

0 commit comments

Comments
 (0)