# \<expanding-list>

{% tabs %}
{% tab title="📗 參考" %}
* [Customized built-in elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#customized_built-in_elements) (MDN), [source code](https://github.com/mdn/web-components-examples/blob/master/expanding-list-web-component/main.js) (GitHub)
* helpers.js 👉 [JS 常用函數](../../browser/dom/querying-elements/dom)
{% endtab %}

{% tab title="📘 手冊" %}
* [node.firstChild](https://developer.mozilla.org/en-US/docs/Web/API/Node/firstChild) - ⭐️ 注意：「任何空白」都會產生 **#text** node，例如：`<p>   <span>`&#x20;
* [element.firstElementChild](https://developer.mozilla.org/en-US/docs/Web/API/Element/firstElementChild) - ⭐️ 如果不想抓到 #text 或 #comment，可使用此屬性。
* [element.insertAdjacentHTML()](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML)
* [DOMTokenList.remove()](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/remove) - 可用在 elem.**classList.remove()**
{% endtab %}

{% tab title="🗣 討論" %}
* [Is there a CSS parent selector?](https://stackoverflow.com/questions/1014861/is-there-a-css-parent-selector)
{% endtab %}

{% tab title="🏗 應用" %}
* [DOM Hierarchy](../../browser/dom/hierarchy)
* 👉 [\<dom-hierarchy>](dom-hierarchy)
{% endtab %}
{% endtabs %}

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

{% tabs %}
{% tab title="ExpandingList" %}
```javascript
/*
    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' });
```
{% endtab %}

{% tab title="HTML" %}
```markup
<h3>DOM Hierarchy</h3>
<button onclick="list.expandAll()">Expand All</button>
<hr>
    
<ul is="expanding-list" id="list">
	<!-- ⭐️ light DOM -->
    <li>EventTarget
		<ul>
			<li>Node
				<ul>
					<li>DocumentFragment
						<ul>
							<li>ShadowRoot</li>
						</ul>
					</li>
					<li>Element 
                            <ul>
                                <li>HTMLElement</li>
                                <li>SVGElement</li>
                            </ul>
                        </li>
                        <li>CharacterData 
                            <ul>
                                <li>Text</li>
                            </ul>
                        </li>
				</ul>
			</li>
		</ul>
	</li>
</ul>
```
{% endtab %}

{% tab title="CSS" %}
```css
/* import Font Awesome */

@import url("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css");

/* for all icons */
.fas, .fab {
    /* border: 1px solid black; */
    font-size: 1rem;
    /* background-color: white; */
    color: hsl(5, 80%, 60%);
    margin-right: 0.5rem;
}

/* closed icon */
.closed .fas, .closed .fab {
    color: hsl(45, 80%, 60%);
}

/* for expanding-list light DOM */

ul {
    list-style-type: none;
    margin-left: 1.7rem;
    padding-left: 0;
}

/* closed folder content invisible */
.closed > ul {
    display: none;
}

/* hint to click */
span {
    cursor: pointer;
}

/* highlight on hover */
span:hover {
    background-color: hsla(60, 80%, 50%, 0.3);
    padding: 0 4px;
    border: 1px dotted black;
    border-radius: 4px;
}
```
{% endtab %}

{% tab title="📗 參考" %}

{% endtab %}
{% endtabs %}
