javascript – Correctly removing nodes from the DOM that have handlers registered

Question:

There is a need to periodically, by means of AJAX , rewrite the contents of the block, by assigning a new innerHTML , while handlers were assigned to the child elements of this block, by means of element.addEventListener , which I do not explicitly delete, and at the same time, after adding elements, I register also new ones. In most cases, the content of the old block is exactly the same as the new block, but I still overwrite it and reassign handlers.

I am interested, when rewriting the child elements of the block, by means of innerHTML , are their declared handlers also removed, along with all the child elements, or do they remain together with the new ones, which I register each time anew?

And if so, does this also happen with element.remove() and parent.removeChild(element) ?

Answer:

Your case does not refer directly to dom elements or listeners in any way. This is a common case in understanding the garbage collector or as it is called Garbage Collection or simply GC .

Its operation is very simple – if an object can be reached by links starting from the root window element, then it cannot be disposed of.

In other words, if you pass a reference to object b to object a and then remove the reference to object a , then object b will not hold it.

let a = { b: null };
let b = { a: a };

a.b = b;
a = null; // объект { b: a } будет удален из памяти.

What happens when we subscribe to events. First you need to remember what an EventDispatcher is –

export default class EventDispatcher {
    constructor(){
        this.handlers = {};
    }

    addEventListener(type, handler){
        let handlerAll = this.handlers[ type ];

        if( ! handlerAll){
            handlerAll = this.handlers[ type ] = [];
        }

        handlerAll.push( handler );
    }
    removeEventListener(type, handler){
        let handlerAll = this.handlers[type];

        if(handlerAll){
            let index = handlerAll.indexOf( handler );

            if(index > -1){
                handlerAll.splice( index, 1 );

                if( ! handlerAll.length) {
                    delete this.handlers[type];
                }
            }
        }
    }
    dispatchEvent(type, event){}
}

This is a very simplistic example that does not contain comments, just because everything is banal to the point. But you should pay special attention to the fact that the passed listener is simply stored in the EventDispatcher . In other words –

let EventDispatcher = { handlerAll: [  ] };
let handler = function(){};
EventDispatcher.handlerAll.push( handler );

EventDispatcher = null; //все, EventDispatcher канул вне небытия
                        // и handler не смог его удержать.

It turns out that if the situation is very simple, almost unrealistic, then after deleting the object from the dom , it will be safely deleted. But it's different! There remains a very high probability that the context in which the subscription took place will remain in memory, and this is even with a better set of circumstances. After all, the main reason the "always remove listeners" rule of thumb was adopted is замыкание , which are the foundation of javascript .

Therefore, I will not describe all the possible causes of leaks, but will only say once again that it is considered good practice to always remove listeners, and indeed to clean objects manually.

Well, to be completely frank with each other, then you need to understand what the Element returned to us upon request is. when we ask to give us the desired element by some identifier, for example document.querySelector( #some-id ); , the program will return an object to us, which is a collection of properties and methods for managing
an object created at a lower level. And when you write like below, you store the reference to the element in a variable –

let element = document.querySelector( `#some-id` );

element.addEventListenr( 'click', element_clickHendler );
function element_clickHendler(){}

Which, in turn, when deleting from the parent using any known method, will delete the object only from the sheet display, in other words, from the screen. But the link will still hold the element object and the listener will be operational as long as there is at least one link to it.

document.querySelector( `#some-id` ).addEventListenr( 'click', element_clickHendler );
function element_clickHendler(){}

If you do as above, then deleting an element by any known means will remove both the element from the sheet display and the element object. Therefore, the listener will also be deleted along with the element object. And all this is due to the fact that the reference to the object returned during the search is not saved.

As for the method of removal, I am more than sure that there is no difference at the program level. After all, an object can always be deleted in only one way, and all the rest of the variety is syntactic sugar for convenient operation.

Scroll to Top