yccteam
yccteam

Reputation: 2311

Custom Elements: monkeypatch disconnected callback

I'm writing a web component enhancer function and I need to run something when the element is removed from the DOM - but I know this after the fact. I've used MutationObserver - but my component is used a lot around the page and multiple mutation observers are causing a performance issue.

Here's an attempt to do this without MutationObserver:

class TestComponent extends HTMLElement {
    disconnectedCallback() {
        console.log('CB');
    }
}

window.customElements.define('test-component', TestComponent);

function enrichComponent(component) {
  const originalDisconnectedCallback = component.disconnectedCallback;
  component.disconnectedCallback = function() {
    originalDisconnectedCallback();
    console.log('CB1');
  }
}

const component = document.createElement('test-component');
enhancer(component);
document.body.appendChild(component);
component.remove(); // logs 'CB' but no 'CB1'

This doesn't work.

Is there a way to 'monkeypatch' disconnectedCallback?

Upvotes: 3

Views: 639

Answers (2)

yccteam
yccteam

Reputation: 2311

I've managed to come up with a "hack" that is working and is less costly than mutation observers.

The idea is to create a component inside the enhancer function, append it to the web component and run the cleanup function from inside the mock component.

Here's an example:

class FormAssociationDisconnectionComponent extends HTMLElement {

    disconnectedCallback() {
        this.dispatchEvent(new Event('disconnected'));
    }
}

window.customElements.define('form-association-disconnection', FormAssociationDisconnectionComponent);

function enrichComponent(component) {
  // ... setup a form and a hidden input we need to cleanup

  const removeListenerElement = document.createElement('form-association-disconnection');
  removeListenerElement.addEventListener('disconnected', () => {
        hiddenInput.remove();
        hostingForm.removeEventListener('reset', resetFormHandler);
  });
  inputElement.appendChild(removeListenerElement);
}

This way, you can run any cleanup you want when your custom element is removed without creating multiple MutationObservers.

Upvotes: 0

GullerYA
GullerYA

Reputation: 1776

I'd break it into two solutions, each closer to the specific use-case:

  • I (still, yep we talked about it) think, that if it is your own code that is creating the hiddenElement within that component - better to do the whole management within the component and not enrich/extend it from the outside (yes, even if you have this pattern repeated for a several components in the whole system)
  • If one absolutely wants to go enhancing the component from outside (say one not owns the class, or came to a multiple inheritance issue) - better to go with a standard event driven approach - disconnectedCallback should dispatch a disconnect event and the listener should be attached to it - otherwise this solution is just not scaleable / open for extensions (think of the case when another teammate need to add some more logic on disconnect, logic that is decoupled from the one found in the first listener)

Upvotes: 2

Related Questions