Home > Back-end >  removeEventListener does not work for touchend and mouseup listeners
removeEventListener does not work for touchend and mouseup listeners

Time:04-19

I've made a class to keep track of differet event listeners in order to make dynamically adding and removing event listeners from an element easier and more streamlined. Stripped down version of the class:

class ElementListenerState {
    /**
     * @param {object} element - pass DOM element to track listeners for (required)
     * @param {[object]} initialListeners - Initial list of Listeners to register (optional)
     * @returns
     */
    constructor(element, initialListeners = []) {
        this.element = element;
        this.currentListeners = initialListeners || [];
    }
    /**
     *
     * @returns {[]} a copy array of currentListeners
     */
    getCurrentListeners() {
        return [...this.currentListeners];
    }

    setCurrentListeners(arr) {
        this.currentListeners = arr;
    }

    removePrevListeners() {
        const prevListeners = this.getCurrentListeners();
        prevListeners.forEach((listener) => {
            this.element.removeEventListener(
                listener.eventType,
                listener.callback
            );
        });
        this.setCurrentListeners([]);
    }

    removeListenerType(type) {
        const currentListeners = this.getCurrentListeners();
        const listenersToRemove = currentListeners.filter(
            (listener) => listener.eventType === type
        );
        // if(!listenersToRemove.length) console.warn(`No listeners with type ${type} found.`)
        listenersToRemove.forEach((listener) => {
            this.element.removeEventListener(
                listener.eventType,
                listener.callback
            );
            currentListeners.splice(
                currentListeners.findIndex((listenerInArr) =>
                    listenerInArr === listener
                )
            );
        });
        this.setCurrentListeners(currentListeners);
    }

    /**
     *
     * @param {object} listener
     * @returns Error code -1 will be returned if listener is already registered, nothing will be returned if method was successful.
     */
    addListener(listener) {
        // Return error code if listener already registered in list
        if (this.getCurrentListeners().includes(listener)) {
            console.warn("Provided listener is already registered in list!");
            return -1;
        }
        this.element.addEventListener(listener.eventType, listener.callback);
        const newListeners = this.getCurrentListeners();
        newListeners.push(listener);
        this.setCurrentListeners(newListeners);
    }
}

I also have a smaller class for generating listeners in the correct format to work with the ElementListenerState class:

class ListenerObject {
    /**
     *
     * @param {string} eventType represents the type of event listener
     * @param {function} handler callback function for event listener
     * @param {[]} args Array of arguments (in proper order) for callback function, defaults to empty array
     */
    constructor(eventType, handler, args = []) {
        this.eventType = eventType;
        this.callback = (e) => handler(e, ...args);
    }
}

The functionality works exactly as expected for both classes when adding and removing MOST event listeners, but if the event listener type is "mouseup" or "touchend", the ListenerObject gets removed from the currentListeners array but does not actually get removed from the DOM element. This issue happens when using the removePrevListeners method and the removeListenerType event.

No error gets printed to the console since all the touchend and mouseup events in my code are doing is removing other event listeners from an ElementListenerState object, but the chrome dev tools show that these events are still attached.

In both methods, the function comes from the callback property of a ListenerObject, so the function should be the exact function that was applied in addListener and not a copy.

I'm not exactly sure where the issue is coming from or why it's only happening with mouseup and touchend events. Anyone have some insight?

For clarity, when applying the mouseup and touchend events in my code I am not specifying any args, but this shouldn't be an issue since I've done the same with some mousemove and touchmove events which do get properly removed by the removeListenerType method.

CodePudding user response:

Answering my own question before closing: As it turns out the issue has nothing to do with the event type, but rather a bad splice method. In my removeListenerType method I have logic to remove found listeners from the copy of the registered list before setting the list to the updated registered listeners. I somehow forgot to add the second parameter to the splice method, unintentionally removing anything in the array that appears AFTER the found listener, preventing removePrevListeners or removeListenerType from working properly on the next call. Embarassing mistake but I guess it happens.

  • Related