Custom elements own connection resources
The same embedded build supported customElements.define, get and whenDefined, while upgrade was unavailable. A selected late-definition test upgraded an existing mounted element during define. The readiness promise resolved afterward with undefined; retrieve the constructor through get rather than treating that result as the constructor.
Keep constructors limited to JavaScript state initialization and super(); attribute changes during construction produced a native error in the earlier test. Register a unique tag once per document. Acquire listeners idempotently in connectedCallback and release exact identities in disconnectedCallback.
Moving between mounted parents delivered disconnect then connect on the same instance, with the new parent already visible to both callbacks. Cleanup must not depend on parentNode being null. Observed-attribute callbacks also ran on a retained detached element; detachment alone does not make the component inert. Reassigning the same observed string was silent, so it is not a forced-refresh mechanism.
Motion and observer callbacks do not own disposal
Interrupted CSS motion can emit cancel instead of end. Tested detachment and display:none canceled animations, and a completed fill animation could still produce later notifications. Use repeatable settlement, exact end/cancel listeners and an explicit disposer; never make view cleanup depend only on animationend.
A MutationObserver can continue observing a retained detached target. Disconnect explicitly, release queued reconciliation work and guard callbacks by owner lifetime. A recorded empty callback after disconnect also means that disconnect is not proof that no previously scheduled callback can execute. Ignore empty records and recheck that targets still belong to the feature.
See engine connection release and resource request ownership for the separate cleanup contracts.