I am creating Custom HTML Element using Web Components
I retrieve some JSON data via a REST Service and want to create Custom Elements dynamically based on the response.
class LibraryFolders extends HTMLElement {
async connectedCallback() {
// Load folders
const foldersResponse = await fetch('/wp-json/prodes-media-library/v1/folders');
const foldersResponseJson = await foldersResponse.json();
foldersResponseJson.forEach(folder => {
document.createElement('library-folder');
});
}
}
customElements.define('library-folders', LibraryFolders);
This works correctly, when creating the element using createElement
it calls the constructor
in the customElement class called LibraryFolder
class LibraryFolder extends HTMLElement {
constructor() {
super();
// logs <library-folder></library-folder>
console.log(this);
// This results eventually into the following error:
// Failed to construct 'CustomElement': The result must not have a parent
document.querySelector('#folders').appendChild(this);
}
}
customElements.define('library-folder', LibraryFolder);
What I try to archieve is to append the library-folder
element to an element called <div id="folders"></div>
as soon the constructor
in class LibraryFolder
is fired.
However, when trying that this results into the following error:
Uncaught DOMException: Failed to construct 'CustomElement': The result must not have a parent
at HTMLDocument.createElement (:1:1536)
at HTMLDocument.document.createElement (:292:44)
at (URL)
at Array.forEach ()
at HTMLElement.connectedCallback (URL)
What am I doing wrong here? There is no information about this error in the documentation, unlike other similar errors.
Thanks to @pointy to send me into the right direction.
An element that adds itself to the DOM would be weird. (For one thing, it wouldn't work independently of a particular page structure.)
The problem was that I was trying to add the element using its own constructor
to the DOM, I also figured out that if you managed to get it working inside the constructor
that it will result into.
Maximum call stack size exceeded.
I managed to fix the issue by creating a function inside the class which can be called later on to append the element to the DOM.
class LibraryFolder extends HTMLElement {
constructor() {
super();
}
appendToDom() {
// Moved this outside the constructor
document.querySelector('#folders').appendChild(this);;
}
}
customElements.define('library-folder', LibraryFolder);
And then in the LibraryFolders
class call that function.
class LibraryFolders extends HTMLElement {
async connectedCallback() {
// Load folders
const foldersResponse = await fetch('/wp-json/prodes-media-library/v1/folders');
const foldersResponseJson = await foldersResponse.json();
foldersResponseJson.forEach(folder => {
const node = document.createElement('library-folder');
// Now it works correctly :)
node.appendToDom();
});
}
}
customElements.define('library-folders', LibraryFolders);
Just to add, this error is correct according to specs.
If result’s parent is not null, then throw a "NotSupportedError" DOMException.
Chrome doesn't give the correct error, but if you try it in Firefox you will get the specified error
For more details check out https://dom.spec.whatwg.org/#dom-document-createelement