Vue3 custom element (Web Component) does not render below Chrome 65 (Android 8 built-in webview)
When isCustomizedBuiltIn
is false, Vue3 passes undefined
as the second argument to document.createElement
through nodeOps.createElement
when the built-in tag name is not used as a custom element. This usage does not work in Chrome versions prior to 66, meaning that the constructor
and connectedCallback
of the custom element will not be triggered.
Interestingly, starting from version 66, the constructor
will not be triggered upon creation, but when appended to the DOM, the constructor
and connectedCallback
will be triggered in sequence, allowing it to function properly. In the latest version, the constructor
will be triggered upon creation, and when appended to the DOM, the connectedCallback
will be triggered.
// file: index.html
// patch the createElement to avoid options is nullish
var _createElement = window.document.createElement;
window.document.createElement = function (tagName, options) {
if (options) {
return _createElement.call(window.document, tagName, options);
} else {
return _createElement.call(window.document, tagName);
}
}
// file: src/custom-element.ts
class CustomElement extends HTMLElement {
constructor() {
super();
console.log("Custom element created");
}
connectedCallback() {
const text = this.dataset["text"];
console.log("Custom element added to page --> ", text);
const div = document.createElement("div");
div.innerText = "custom-element with text: " + text;
this.appendChild(div);
}
}
customElements.define("custom-element", CustomElement);
Chrome/65.0.3325.144
Chrome/66.0.3347.0
Chrome/118.0.0.0
chromium version 121.0.61313.1
Chrome/65.0.3325.144
Chrome/66.0.3347.0
Chrome/118.0.0.0
Test Code
// define custom element
class CustomElement extends HTMLElement {
constructor() {
super();
console.log("Custom element created");
}
connectedCallback() {
const text = this.dataset["text"];
console.log("Custom element added to page --> ", text);
const div = document.createElement("div");
div.innerText = "custom-element with text: " + text;
this.appendChild(div);
}
}
customElements.define("custom-element", CustomElement);
// create element with options undefined
e = document.createElement("custom-element", undefined)
// Chrome/65.0.3325.144 ❌ constructor not executed
// Chrome/66.0.3347.0 ⚠️ constructor not executed
// Chrome/118.0.0.0 ✅ constructor executed
// create element with options null
document.createElement("custom-element", null)
// Chrome/65.0.3325.144 ❌ constructor not executed
// Chrome/66.0.3347.0 ⚠️ constructor not executed
// Chrome/118.0.0.0 ✅ constructor executed
// create element with options string empty
document.createElement("custom-element", "")
// Chrome/65.0.3325.144 ❌ constructor not executed
// Chrome/66.0.3347.0 ⚠️ constructor not executed
// Chrome/118.0.0.0 ✅ constructor executed
// create element with out options
document.createElement("custom-element")
// Chrome/65.0.3325.144 ✅ constructor executed
// Chrome/66.0.3347.0 ✅ constructor executed
// Chrome/118.0.0.0 ✅ constructor executed
// create element with options {is: ""}
document.createElement("custom-element", {is: ""})
// Chrome/65.0.3325.144 ✅ constructor executed
// Chrome/66.0.3347.0 ✅ constructor executed
// Chrome/118.0.0.0 ✅ constructor executed
// create element with options empty object
document.createElement("custom-element", {})
// Chrome/65.0.3325.144 ✅ constructor executed
// Chrome/66.0.3347.0 ✅ constructor executed
// Chrome/118.0.0.0 ✅ constructor executed
// test appent to dom
testDiv = document.getElementById("testDiv")
testDiv.appendChild(e)
// Chrome/65.0.3325.144 ❌ constructor not executed && connectedCallback not executed
// Chrome/66.0.3347.0 ✅ constructor executed && connectedCallback executed
// Chrome/118.0.0.0 ✅ connectedCallback executed