There are a set of rules on what you can and can not do in the constructor of a Web Component. They are below.
But think of this:
A component can be created one of three ways:
- Part of initial page/using innerHTML: When the browser loads the page or when using
innerHTML
you can add attributes and children to a component as part of the page load or part of innerHTML
.
parent.innerHTML = '<super-hero name="Thor"><super-weapon value="Mjolnir"></super-weapon></super-hero>'.
- You can call
document.createELement
to create an element. You can not add attributes or children until after the element is created.
let superHero = document.createElement('super-hero');
let superWeapon = document.createElement('super-weapon');
superHero.setAttribute('name', 'Thor');
superWeapon.setAttribute('value', 'Mjolnir');
superHero.appendChild(superWeapon);
parent.appendChild(superHero)
- You can instantiate an object using
new
. Just like document.createElement
you must wait until after the element is created before adding attributes and children.
let superHero = new SuperHero();
let superWeapon = new SuperWeapon();
superHero.setAttribute('name', 'Thor');
superWeapon.setAttribute('value', 'Mjolnir');
superHero.appendChild(superWeapon);
parent.appendChild(superHero)
Once the component is created it is added to the DOM and this is when your component's connectedCallback
is called.
And all three of these ways really come down to instantiating with new
. document.createElement
calls CustomElementRegistry.get
to get the constructor for that element and then uses new
to create the object.
And innerHTML
parses the HTML and then either calls document.createElement
or uses new
to create the object.
But, in doing so, there are NO attributes or children when the constructor for your element is called. In fact, there may not be any children or attributes when connectedCallback
is called. This is one reason that observedAttributes
and attributeChangedCallback
were added in the spec.
The one thing missing from the spec is knowing is someone has added or changed children either before or after the component was added to the DOM. But, if you really want to know when the children change you can use MutationObserver
.
This is why no children or attributes exists in your constructor. They have not bee added yet.
Now on to the rules:
When authoring custom element constructors, authors are bound by the following conformance requirements:
A parameter-less call to super() must be the first statement in the constructor body, to establish the correct prototype chain and this value before any further code is run.
A return statement must not appear anywhere inside the constructor body, unless it is a simple early-return (return or return this).
The constructor must not use the document.write() or document.open() methods.
The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.
The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createElementNS methods.
In general, work should be deferred to connectedCallback as much as possible—especially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that is truly one-time will need a guard to prevent it from running twice.
In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.
Several of these requirements are checked during element creation, either directly or indirectly, and failing to follow them will result in a custom element that cannot be instantiated by the parser or DOM APIs.