@@ -102,6 +102,64 @@ const create_from_string = (string) => {
102102 return div . firstChild ;
103103} ;
104104
105+ // Event listener registration for easy-to-remove event listeners.
106+ // once Safari supports the ``signal`` option for addEventListener we can abort
107+ // event handlers by calling AbortController.abort().
108+ const event_listener_map = { } ;
109+
110+ /**
111+ * Add an event listener to a DOM element under a unique id.
112+ * If a event is registered under the same id for the same element, the old hander is removed first.
113+ *
114+ * @param {DOM Node } el - The element to register the event for.
115+ * @param {string } event_type - The event type to listen for.
116+ * @param {string } id - A unique id under which the event is registered.
117+ * @param {function } cb - The event handler / callback function.
118+ * @param {Object } opts - Options for the addEventListener API.
119+ *
120+ */
121+ const add_event_listener = ( el , event_type , id , cb , opts = { } ) => {
122+ if ( ! el ?. addEventListener ) {
123+ return ; // nothing to do.
124+ }
125+ remove_event_listener ( el , id ) ; // do not register one listener twice.
126+
127+ if ( ! event_listener_map [ el ] ) {
128+ event_listener_map [ el ] = { } ;
129+ }
130+ event_listener_map [ el ] [ id ] = [ event_type , cb , opts . capture ? opts : undefined ] ; // prettier-ignore
131+ el . addEventListener ( event_type , cb , opts ) ;
132+ } ;
133+
134+ /**
135+ * Remove an event listener from a DOM element under a unique id.
136+ *
137+ * @param {DOM Node } el - The element to register the event for.
138+ * @param {string } id - A unique id under which the event is registered.
139+ *
140+ */
141+ const remove_event_listener = ( el , id ) => {
142+ if ( ! el ?. removeEventListener ) {
143+ return ; // nothing to do.
144+ }
145+ const el_events = event_listener_map [ el ] ;
146+ if ( ! el_events ) {
147+ return ;
148+ }
149+ let entries ;
150+ if ( id ) {
151+ // remove event listener with specific id
152+ const entry = el_events [ id ] ;
153+ entries = entry ? [ entry ] : [ ] ;
154+ } else {
155+ // remove all event listeners of element
156+ entries = Object . entries ( el_events ) ;
157+ }
158+ for ( const entry of entries || [ ] ) {
159+ el . removeEventListener ( entry [ 0 ] , entry [ 1 ] , entry [ 2 ] ) ;
160+ }
161+ } ;
162+
105163const dom = {
106164 toNodeArray : toNodeArray ,
107165 querySelectorAllAndMe : querySelectorAllAndMe ,
@@ -113,6 +171,8 @@ const dom = {
113171 get_parents : get_parents ,
114172 is_visible : is_visible ,
115173 create_from_string : create_from_string ,
174+ add_event_listener : add_event_listener ,
175+ remove_event_listener : remove_event_listener ,
116176} ;
117177
118178export default dom ;
0 commit comments