-
Notifications
You must be signed in to change notification settings - Fork 38
Web Component Limitations
Checklist » Web Component Limitations
This page attempts to capture limitations of the current web component APIs that make it difficult or impossible to create a web component that's as good as a standard HTML element.
The Requirements for custom element constructors dictate that a constructor may not add attributes to a new element. Since standard HTML elements created with document.createElement()
never have attributes, the expectation is that custom elements shouldn't either. However, there are situations in which a custom element can accomplish a desired result only through adding attributes to itself.
The canonical example here might be ARIA support, which is strictly managed through attributes. A list-like custom element might want to provide a default ARIA role by setting role="listbox"
on itself during initialization. However, per the above rules, this cannot be done in the constructor:
class ListBox extends HTMLElement {
constructor() {
super();
this.setAttribute('role', 'listbox'); // This throws an exception.
}
}
The next-best way to handle this situation might be a connectedCallback
.
class ListBox extends HTMLElement {
connectedCallback() {
this.setAttribute('role', 'listbox');
}
}
However, this is suboptimal, for two reasons. First, it's surprising that the element will magically gain an attribute when it's added to the document:
let listBox = document.createElement('basic-list-box');
let roleBeforeAdding = listBox.getAttribute('role'); // null
document.appendChild(listBox);
let roleAfterAdding = listBox.getAttribute('role'); // Now equals 'listbox'. Surprise!
Second, the above component definition could overwrite attributes that were added to the element after it was constructed and before it was added to the document:
let listBox = document.createElement('basic-list-box');
listBox.setAttribute('role', 'tabs'); // Set custom role
document.body.appendChild(listBox); // connectedCallback will overwrite role!
To avoid this problem, a custom element should check if it an attribute has already been set before applying a default value:
class ListBox extends HTMLElement {
connectedCallback() {
if (!this.getAttribute('role')) {
this.setAttribute('role', 'listbox');
}
}
}
let listBox = document.createElement('basic-list-box');
listBox.setAttribute('role', 'tabs'); // Set custom role
document.body.appendChild(listBox); // connectedCallback leaves role alone
This avoids the second problem above, but is still subject to the first problem of potentially surprising behavior.
For a deeper analysis of this problem, including proposals for possible long-term fixes in the platform, see Gap Analysis: Accessibility.