WebComponent
Defined in: web-components-integration/src/WebComponent.ts:263
Introduction
Section titled “Introduction”This is the base web components class and the whole web components implementation.
This is just a custom element with a number of necessary features missing from the standard custom elements.
Implementation goals
Section titled “Implementation goals”This web components implementation was designed with the following goals in mind:
- Improve upon custom elements:
- Implement necessary features that are missing from the standard custom elements.
- Maintain compliance with the custom elements specification.
- Maintain compatibility with other libraries and frameworks without additional setup.
- Keep implementation minimal and size small:
- Don’t pollute application’s bundle with the code that most likely won’t be used.
- Don’t implement features for the sake of having features.
- Achieve maximum performance.
As you can see, this is not a framework. There are no quality-of-life abstractions and features found in major frameworks like Lit. Ultimately, this is a bare minimum to ship off web components integration.
When to use this
Section titled “When to use this”When you are:
- Occasionally using web components (or just
vite-awesome-svg-loaderalone). - Building a wrapper around
vite-awesome-svg-loader. - Having other reasons to use a minimal web components implementation like this one.
When to NOT use this
Section titled “When to NOT use this”When you are:
-
Planning or already using a dedicated library for managing web components.
Just use that library.
-
Planning to build more web components.
Consider using a dedicated library. It will pay off in the long run.
Behavioral differences from custom elements
Section titled “Behavioral differences from custom elements”-
All attributes are observed automatically. No need for WebComponent.observedAttributes.
-
WebComponent.attributeChangedCallback is called when component has initialized and when any attribute’s value has actually changed. Setting same value doesn’t count as a change.
-
Properties can be synchronized with attributes by defining them in WebComponent.props.
Core principles
Section titled “Core principles”-
Use constructors to initialize state. But do not use properties defined in WebComponent.props in a constructor because that will trigger an attribute setter which renders constructor invalid (per custom elements specification).
-
Use WebComponent.connectedCallback to initialize markup.
-
Use WebComponent.attributeChangedCallback to track property/attribute changes. Do not define your own getters and setters, they will be overridden.
-
Prefer inheritance over composition. Yes, this is an anti-pattern. Web components are actual elements. The more components you nest, the larger and slower to process DOM becomes. If you want composition, either sacrifice performance or use a specialized library/framework.
-
When using a web component inside another web component, call
define({ noRedefinitionError: true })and use a constructor instead ofdocument.createElement().Suppress a redefinition error because in most cases user will not specify different tag and would not expect an error if they haven’t defined a dependency component.
Use a constructor because user still might decide to use a custom tag.
Ultimately, user will get an error, if the definition is invalid.
-
If you’ve encountered a problem that must be resolved immediately by modifying the internals, everything’s here for you. Just use
// @ts-ignoreto suppress the errors. After the hotfix, please file an issue and explain your case.
Creating web components
Section titled “Creating web components”Let’s create a simple BusinessCard web component:
class BusinessCard extends WebComponent implements CustomElement { // Define class properties that will be synced with attributes
static readonly props = ["personName", { name: "orgName", default: "Not affiliated" }];
// Declare properties for type-checking to work. You may actually define them, but they will be overridden // with proper getters and setters, so don't create empty overhead.
declare personName?: string; declare orgName: string;
// Add markup
connectedCallback() { super.connectedCallback();
if (this.children.length) { return }
const personName = document.createElement("div"); personName.className = "person-name"; this.appendChild(personName);
const orgName = document.createElement("div"); orgName.className = "org-name"; this.appendChild(orgName); }
// Track changes
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null) { switch (name) { // props are tracked: case "personName": case "orgName": this.getElementsByClassName(name)[0]!.innerText = newValue || "-"; break;
// Every other attribute is also tracked: case "data-some-attr": console.log(oldValue, newValue); break; } }
// Provide default tag for definition (optional, but recommended)
define(options?: WebComponentDefinitionOptions) { super.define({ ...options, tag: options.tag || "business-card" }); }}
// Define a custom element
BusinessCard.define();Using web components
Section titled “Using web components”Let’s instantiate and mount a BusinessCard web component that we’ve created:
// Create a web component instance:
const card = new BusinessCard();// or:const card = document.createElement("business-card");
// Add component to the DOM:
document.getElementById("card")!.appendChild(card);
// Change properties:
card.firstName = "John";// or:card.setAttribute("first-name", "John"); // Notice that attribute is in snake-case while property is in camelCaseExtending web components
Section titled “Extending web components”Let’s create a CoolBusinessCard component by extending BusinessCard component:
class CoolBusinessCard extends BusinessCard implements CustomElement { // Define new properties. Inherited properties will be defined and copied to "props" automatically. // Note: it is impossible to redefine inherited properties because parent relies on their behavior.
static readonly props = [ { name: "coolness", default: "120%" }, ];
declare coolness: string;
// Provide default tag for definition
define(options: WebComponentDefinitionOptions = {}) { super.define({ ...options, tag: options.tag || "cool-business-card" }); }}Extends
Section titled “Extends”HTMLElement
Extended by
Section titled “Extended by”Implements
Section titled “Implements”CustomElement
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new WebComponent():
WebComponent
Defined in: web-components-integration/src/WebComponent.ts:320
Returns
Section titled “Returns”WebComponent
Overrides
Section titled “Overrides”HTMLElement.constructor
Properties
Section titled “Properties”_state
Section titled “_state”
protected_state:WebComponentState="uninitialized"
Defined in: web-components-integration/src/WebComponent.ts:284
Current component state. See WebComponentState for the details.
observedAttributes?
Section titled “observedAttributes?”
staticoptionalobservedAttributes:string[]
Defined in: web-components-integration/src/WebComponent.ts:270
Doesn’t do anything. All attributes are already observed. This property will be deleted when custom element will be defined.
This property is recognized for compatibility reasons only.
staticprops: (string|WebComponentProp)[] =[]
Defined in: web-components-integration/src/WebComponent.ts:279
Properties that will be synced with attributes.
Properties must be in camelCase. Synchronized attributes will be in kebab-case.
To not confuse cases, prefer single-word names.
Methods
Section titled “Methods”attributeChangedCallback()?
Section titled “attributeChangedCallback()?”
optionalattributeChangedCallback(…args):void
Defined in: web-components-integration/src/WebComponent.ts:316
Called when:
-
Component has just finished initialization. Then:
- _state property will be
attrs-first-change. oldValueargument will benull.newValuewill have a default value ornull, if there’s no default.- Thus, following may be true:
oldValue === newValue && newValue === null.
- _state property will be
-
Any of the element’s attributes has actually changed (unlike default custom elements’ behavior where this method is called even if the same value has been set). Then:
- _state property will be
normal. oldValueandnewValuewill be always different.
- _state property will be
Parameters
Section titled “Parameters”…[string, string | null, string | null]
Returns
Section titled “Returns”void
Implementation of
Section titled “Implementation of”CustomElement.attributeChangedCallback
connectedCallback()
Section titled “connectedCallback()”connectedCallback():
void
Defined in: web-components-integration/src/WebComponent.ts:406
Called when the element is added to a document.
If overridden, super.connectedCallback() must be the first statement.
Returns
Section titled “Returns”void
Implementation of
Section titled “Implementation of”CustomElement.connectedCallback
getAttribute()
Section titled “getAttribute()”getAttribute(
name):string|null
Defined in: web-components-integration/src/WebComponent.ts:412
The getAttribute() method of the element.
Parameters
Section titled “Parameters”string
Returns
Section titled “Returns”string | null
Implementation of
Section titled “Implementation of”CustomElement.getAttribute
Overrides
Section titled “Overrides”HTMLElement.getAttribute
removeAttribute()
Section titled “removeAttribute()”removeAttribute(
name):void
Defined in: web-components-integration/src/WebComponent.ts:423
The Element method removeAttribute() removes the attribute with the specified name from the element.
Parameters
Section titled “Parameters”string
Returns
Section titled “Returns”void
Implementation of
Section titled “Implementation of”CustomElement.removeAttribute
Overrides
Section titled “Overrides”HTMLElement.removeAttribute
setAttribute()
Section titled “setAttribute()”setAttribute(
name,value):void
Defined in: web-components-integration/src/WebComponent.ts:419
The setAttribute() method of the Element interface sets the value of an attribute on the specified element.
Parameters
Section titled “Parameters”string
string
Returns
Section titled “Returns”void
Implementation of
Section titled “Implementation of”CustomElement.setAttribute
Overrides
Section titled “Overrides”HTMLElement.setAttribute
toggleAttribute()
Section titled “toggleAttribute()”toggleAttribute(
name,force?):boolean
Defined in: web-components-integration/src/WebComponent.ts:427
The toggleAttribute() method of the present and adding it if it is not present) on the given element.
Parameters
Section titled “Parameters”string
force?
Section titled “force?”boolean
Returns
Section titled “Returns”boolean
Implementation of
Section titled “Implementation of”CustomElement.toggleAttribute
Overrides
Section titled “Overrides”HTMLElement.toggleAttribute
_ctor()
Section titled “_ctor()”
protected_ctor(): typeofWebComponent
Defined in: web-components-integration/src/WebComponent.ts:485
Returns a constructor of this class
If you need to refer to your own class, type it like so:
class MyClass extends WebComponent { declare protected _ctor: () => typeof MyClass}Returns
Section titled “Returns”typeof WebComponent
constructor of this class
_init()
Section titled “_init()”
protected_init():void
Defined in: web-components-integration/src/WebComponent.ts:353
Initializes component state.
Custom elements may not add attributes or children in a constructor, so separate initialization is required. Thus, this method is called whenever an attribute is accessed or in a microtask after component’s constructor has run.
Initialization is performed only once.
Returns
Section titled “Returns”void
define()
Section titled “define()”
staticdefine(options):void
Defined in: web-components-integration/src/WebComponent.ts:508
Defines a custom element for this web component.
Won’t throw an error if called multiple times with the same tag.
Will throw an error (unless suppressed via BasicWebComponentDefinitionOptions.noRedefinitionError) if defined under a different tag.
Parameters
Section titled “Parameters”options
Section titled “options”BasicWebComponentDefinitionOptions
Definition options
Returns
Section titled “Returns”void
_initClass()
Section titled “_initClass()”
protectedstatic_initClass():void
Defined in: web-components-integration/src/WebComponent.ts:527
Initializes class itself. Called by define and by the constructor. Initialization happens only once.
Returns
Section titled “Returns”void