Search code examples
javascriptes6-classcustom-element

Failed to construct 'CustomElement': The result must not have a parent


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.


Solution

  • 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