Custom elements expose a small set of lifecycle callbacks that the browser invokes at well-defined moments. This reference lists each one, when it fires, and what work belongs there.
How it works
You define callbacks as methods on a class extending HTMLElement:
class MyEl extends HTMLElement {
static get observedAttributes() { return ["value"]; }
constructor() { super(); this.attachShadow({ mode: "open" }); }
connectedCallback() { /* in the DOM — render, add listeners */ }
disconnectedCallback() { /* removed — clean up listeners/timers */ }
attributeChangedCallback(name, oldV, newV) { /* observed attr changed */ }
adoptedCallback() { /* moved to a new document */ }
}
customElements.define("my-el", MyEl);
The ordering rule that trips people up: the constructor may run before the element’s attributes and children exist (or before it is even in a document), so it must not touch them. Anything that reads the DOM, attributes, or parent belongs in connectedCallback.
Tips and notes
connectedCallback/disconnectedCallbackcan fire multiple times as the node moves around the DOM. Make connect logic idempotent; always remove listeners and timers on disconnect.attributeChangedCallbackonly fires for attributes named in the staticobservedAttributesarray — and it fires once per such attribute already set at upgrade time.- Attach the shadow root in the constructor (it has no DOM dependency); render into it on connect.
- Use
adoptedCallbackonly when you move nodes between documents (rare).