<expanding-list>

  • 這個範例完全使用 light DOM 的內容,並沒有用到 shadow DOM。

/*
    Customized built-in elements
    ----------------------------
    https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements

    Terms used in the following code
    --------------------------------
    • folder: <li> containing <ul>
    • folder content: child <ul> (excluding host <ul>)
    • folder title: <span> added by JS
*/

// customized built-in element
class ExpandingList extends HTMLUListElement {

    constructor() {

        // initialize HTMLUListElement
        super();

        // ⭐️ all folders
        this.folders = Array
            .from(this.$all('li'))
            .filter(li => li.$all('ul').length > 0); 

        // for each folder
        this.folders.forEach(li => {

            // folder is closed by default
            li.attr('class', 'closed');

            // ⭐️ wrap <li>'s text in <span> in order to
            //    assign style and event handlers to the <span>
            let textNode = li.firstChild;       // `Text` node

            const span = tag('span', {
                textContent: textNode.textContent,
            });

            // decorate with Font Awesome (folder icon)
            span.insertAdjacentHTML('afterbegin', 
                '<i class="fas fa-folder"></i>'
            );
            
            // add click handler
            span.onclick = function(e){

                const span = e.target;              // folder title (<span>)
                const li = span.parentNode;         // folder (<li>)
                const i = span.firstElementChild;   // folder icon (<i>)

                li.classList.toggle('closed');      // toggle folder status

                // toggle folder icon
                i.classList.toggle('fa-folder-open');
                i.classList.toggle('fa-folder');
            };
            
            // prepend <span>, remove textNode
            li.prepend(span);
            textNode.remove();
        });
    }// end: constructor()

    _openFolder(li){
        // change folder status to open
        li.classList.remove('closed');
        // change to open folder icon
        const i = li.$('span > i');
        i.classList.add('fa-folder-open');
        i.classList.remove('fa-folder');
    }

    // expand all folders
    expandAll(){
        this.folders.forEach(li => this._openFolder(li));
    }

}// end: ExpandingList

// register <expanding-list>
customElements.define('expanding-list', ExpandingList, { extends: 'ul' });

Last updated