I have a situation similar to the example below: from a template
element, I clone a node tree containing custom elements. One custom element is passed data during initialization, represented here by the line infobox.setData(getData())
. The function I use to pass the data (setData
) is added by my custom class, so I make sure the custom element is upgraded before calling it, by passing the node tree to customElements.upgrade
. (I have also tried passing infobox
itself.)
Unfortunately, when I try running my code or the example below I receive the error infobox.setData is not a function
. I have confirmed infobox instanceof InfoBox
is false and the element has no custom properties or methods prior to being connected to the document, so it seems customElements.upgrade
is not upgrading the elements in the tree. What might be preventing it from doing so?
document.getElementById('addWidget').onclick = addWidget
class InfoBox extends HTMLElement {
_data = ""
connectedCallback() {
this.render()
}
setData(val) {
this._data = val
this.render()
}
render() {
if (!this?.isConnected) {
return
}
this.replaceChildren(...this._data.split(' ').map(datum => {
const el = document.createElement('span')
el.innerText = datum
return el
}))
}
}
customElements.define('info-box', InfoBox)
function addWidget() {
const widget = document.getElementById('widgetTemplate').content.cloneNode(true)
const infobox = widget.querySelector('info-box')
customElements.upgrade(widget)
console.assert(!(infobox instanceof InfoBox))
console.assert(!('setData' in infobox))
try {
// TypeError: infobox.setData is not a function
infobox.setData(getData())
} catch {}
document.getElementById('container').append(widget)
// works because infobox was upgraded after being added to the document
infobox.setData(getData())
}
function getData() {
return ('lorem ipsum dolor sit amet consectetur adipiscing elit proin at ' +
'vestibulum enim vestibulum ante ipsum primis in faucibus orci luctus')
}
#container {
background: lightgrey;
padding: 2em;
}
info-box {
display: flex;
flex-flow: row wrap;
gap: .5em;
padding: .5em;
background: darkgrey;
}
info-box>span {
background: lightblue;
border-radius: .5em;
padding: .5em;
}
<template id="widgetTemplate">
<details>
<info-box></info-box>
</details>
</template>
<button id="addWidget">Add</button>
<div id="container"></div>