Issue at hand involves two methods; one for initializing event listeners and the other for deleting them. Upon deletion, successful messages in the console confirm removal from the component's listener array. However, post-deletion, interactions with the UI reveal that the listeners were not completely deleted. Curiously, DevTools' "Event Listeners" tab also displays their presence. Manually deleting via DevTools rectifies this issue. Suspecting a context loss in the removeDOMListeners
method despite the cleared array. Seeking advice on this matter.
Output following removal of event listeners:
Listeners before removal: 1 Listeners after removal: 0
DOMListener.ts:
export abstract class DOMListener<T extends DOMElement> {
protected readonly $root: T;
protected readonly listeners: string[];
protected constructor($root: T, listeners: string[] = []) {
if (!$root) {
throw new Error(`No $root provided for DomListener!`);
}
this.$root = $root;
this.listeners = listeners;
}
[key: string]: any;
public initDOMListener(): void {
this.listeners.forEach((listener) => {
const method = prefixSetter('on', listener);
if (!this[method]) {
const name = this.name || '';
throw new Error(`Method ${method} is not implemented in ${name} Component`);
}
this[method] = this[method].bind(this);
if (this.$root && this.$root.on) {
this.$root.on(listener, this[method]);
console.log(listener)
}
});
}
public removeDOMListener(): void {
console.log('Listeners before removal:', this.listeners.length);
this.listeners.forEach((listener) => {
const method = prefixSetter('on', listener);
const listenerFunction = this[method].bind(this);
if (this.$root && this.$root.off) {
this.$root.off(listener, listenerFunction);
}
});
this.listeners.length = 0;
console.log('Listeners after removal:', this.listeners.length);
}
}
DOM.ts:
export class DOM {
public $el: HTMLElement;
constructor(selector: string | HTMLElement) {
this.$el = typeof selector === 'string'
? document.querySelector(selector)!
: selector;
}
public on(eventType: string, callback: EventListenerOrEventListenerObject) {
this.$el.addEventListener(eventType, callback);
}
public off(eventType: string, callback: EventListenerOrEventListenerObject) {
this.$el.removeEventListener(eventType, callback);
}
public append(node: DOM | HTMLElement): DOM {
if (node instanceof DOM) {
node = node.$el
}
this.$el.append(node)
return this
}
}
export function $(selector: string | HTMLElement): DOM {
const el = typeof selector === 'string'
? document.querySelector(selector)
: selector
if (!el) {
throw new Error(`Element not found: ${selector}`)
}
return new DOM(el as HTMLElement)
}
$.create = (tagName: string, classes = ''): DOM => {
const el = document.createElement(tagName)
if (classes) {
el.classList.add(classes)
}
return $(el)
};
Component.ts:
export class Header extends BaseComponent {
static className = 'header'
constructor($root: HTMLElement) {
super($root, {
name: 'Header',
listeners: ['input']
});
console.log($root)
}
toHTML(): string {
return `
<input type="text" class="header-input" value="Sheet name" />
<div>
<div class="header-button">
<i class="material-icons">delete</i>
</div>
<div class="header-button">
<i class="material-icons">exit_to_app</i>
</div>
</div>
`;
}
public onInput(e: Event): void {
const $target = e.target as HTMLInputElement;
if ($target.closest('.header-input')) {
console.log('Header input val:' + $target.value);
}
}
}
BaseComponent.ts:
export class BaseComponent extends DOMListener<any> {
name: string;
constructor($root: string | HTMLElement, options: {name?: string; listeners?: string[]} = {}) {
if (!$root) {
throw new Error('No $root provided for BaseComponent');
}
super($root, options.listeners);
this.name = options.name || '';
}
toHTML(): string {
return '';
}
init(): void {
this.initDOMListener()
}
destroy(): void {
this.removeDOMListener()
}
}