Search code examples
javascripteventsecmascript-6scoping

Event delegate - Retrieve instance of class that a DOM element is a property of


I'm learning the ways of scoping and inheritance and am having a little difficulty retrieving the instance of a class of which a DOM element is a property. There is a Parent and lets say there can be unlimited Child(ren). Is there any way of getting the instance via an event delegate? Hopefully the following will help explain:

class Parent {
    constructor() {
        this.element = document.createElement('div');
        this.element.setAttribute('class', 'parent');

        // add elements from instances
        this.element.appendChild(new Child(1, this).element);
        this.element.appendChild(new Child(2, this).element);
        this.element.appendChild(new Child(3, this).element);

        // event delegate
        this.element.addEventListener('click', (e) => {
            // would like to get the particular instance of the class "Child" that was clicked
        });
    }

    something(id) {
        console.log('something', id);
    }
}

class Child  {
    constructor(id, parentReference) {
        this.id = id;
        this.element = document.createElement('div');
        this.element.setAttribute('class', 'child');

        // would like to avoid adding a click event for each and use the parent as delegate
        this.element.addEventListener('click', () => {
            parentReference.something(this.id)
        });
    }
}

Hope this makes sense, welcome any and all feedback.

Also on another note, since I'm not storing the instances anywhere, only creating and appending an element to the DOM, will the Child instance remain intact as long as the DOM element remains rendered and other properties get used then be garbage collected? Should they be stored elsewhere, like an array to be safe?

Thanks in advance!


Solution

  • Looks like what you're trying to do is actually Web Components : you could just make your class extend HTMLElement and benefit from the whole lifecycle which comes natively with custom elements.

    For example, you could have a Parent component which holds some Child components. As Parent and Child classes would be real HTMLElements, if you register a click event listener in the Parent object, you will receive click events triggered on the childs. And the clicked child instance will be available in the target property of the click event.

    class ParentElement extends HTMLElement {
        constructor() {
            super();
            this.classList.add('parent');
    
            // add elements from instances
            this.innerHTML = `
                <child-element data-sequence="1"></Child>
                <child-element data-sequence="2"></Child>
                <child-element data-sequence="3"></Child>
            `;
    
            // event delegate
            this.addEventListener('click', (e) => {
                // Child instance is available in the event itself
                const child = e.target;
                this.something(child.id);
            });
        }
    
        something(id) {
            console.log('something', id);
        }
    }
    window.customElements.define('parent-element', Parent);
    
    class ChildElement extends HTMLElement {
        constructor() {
            super()
            this.id = this.dataset.sequence;
            this.classList.add('child');
        }
    }
    window.customElements.define('child-element', ChildElement);
    

    Then you can just append the parent element into the dom :

    document.body.appendChild(new ParentElement());
    

    (Be careful, i didn't tested the previous example, this is just to give you an idea)

    But maybe i get it wrong. In this case, i would suggest to avoid keeping a reference to a dom element into custom objects as it would probably be easy to get memory leaks (if you re render some portion of your page but keep references to Parent or Child instances then you'll also keep a reference to the corresponding dom node). Couldn't you go the other way around by attaching your custom objects onto dom nodes (a bit like a controller ?)

    Hope this help,