1. Autonomous Elements Work Everywhere Custom Elements Are Supported
Autonomous custom elements use their own tag names, such as <ui-tips>. Safari supports this category.
class UiTips extends HTMLElement {
connectedCallback() {
this.textContent = 'Autonomous custom element upgraded successfully.';
}
}
customElements.define('ui-tips', UiTips);
2. Customized Built-In Elements Extend Native HTML
The elegant form is <button is="ui-action">, <input is="ui-color">, or similar. Safari needs compatibility handling for this pattern.
class UiAction extends HTMLButtonElement {
connectedCallback() {
this.addEventListener('click', () => {
this.textContent = 'Native button behavior preserved';
});
}
}
customElements.define('ui-action', UiAction, {
extends: 'button'
});
If this browser does not support customized built-ins, the support indicator will show that the button was not upgraded.
3. Detect the Feature, Not the Browser
Instead of asking whether the browser is Safari, create a tiny customized built-in element and check whether it upgrades.
class AnyClass extends HTMLBRElement {
constructor() {
super();
this.someMethod = true;
}
}
if (!customElements.get('any-class-demo')) {
customElements.define('any-class-demo', AnyClass, {
extends: 'br'
});
}
const isSupportBuiltIn = document.createElement('br', {
is: 'any-class-demo'
}).someMethod;
4. Use a Compatibility Branch When Support Is Missing
A real project can load a polyfill. This self-contained demo uses the same feature flag to switch between native behavior and an explanatory fallback.
const supportMessage = isSupportBuiltIn
? 'Use customized built-in elements directly.'
: 'Load a built-in custom elements polyfill before components.';
document.querySelector('#compatMessage').textContent = supportMessage;
Current branch
Checking support...
Why this matters
Business code can keep native tags while compatibility code handles unsupported browsers.
5. Wait for a Connected Event Before Calling Component Methods
Polyfilled customized built-ins can initialize later than native ones. Dispatching a custom event from connectedCallback gives business code a reliable hook.
class ReadyPanel extends HTMLElement {
connectedCallback() {
this.innerHTML = '<strong>ReadyPanel</strong><p>Waiting for method call.</p>';
this.dispatchEvent(new CustomEvent('connected', {
detail: { type: 'ready-panel' }
}));
}
someMethod() {
this.querySelector('p').textContent =
'someMethod() ran after initialization.';
}
}
customElements.define('ready-panel', ReadyPanel);
document.querySelector('ready-panel')
.addEventListener('connected', function() {
this.someMethod();
});