# \<custom-menu>

{% tabs %}
{% tab title="📗 參考" %}
* [Menu example](https://javascript.info/slots-composition#menu-example) - js.info
{% endtab %}

{% tab title="📘 手冊" %}
* [elem.classList](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) ([`DOMTokenList`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList)) 👉 [DOM Hierarchy](../../browser/dom/hierarchy)
  * methods: [`add()`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/add), [`remove()`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/remove), [`replace()`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/replace), [`toggle()`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle) , [`contains()`](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/contains) ...
{% endtab %}
{% endtabs %}

:floppy\_disk: [replit](https://replit.com/@pegasusroe/lesscustom-menugreater#script.js), [codepen](https://codepen.io/lochiwei/pen/LYLxwKp?editors=1000)

{% tabs %}
{% tab title="JS" %}
```javascript
/* ---------- helper methods ---------- */

// root.$()
DocumentFragment.prototype.$ = function(selector){
    return this.querySelector(selector);
};

// ⭐️ get/set attribute
//    get: elem.attr(name)
//    set: elem.attr(name, value)
Element.prototype.attr = function(name, value=undefined){
    if(value !== undefined){ this.setAttribute(name, value) };
    return this.getAttribute(name) || undefined;
};

/* ---------- custom element ---------- */

class CustomMenu extends HTMLElement {

    // connected to document
    connectedCallback() {

        // attach shadow root
        let root = this.attachShadow({mode: 'open'});

        // ⭐️ clone nodes from <template>
        let clone = tmpl.content.cloneNode(true);
        root.append( clone );

        this.menu = root.$('.menu');
        this.menuTitle = root.$('slot[name="title"]');

        // we can't select light DOM nodes, 
        // so let's handle clicks on the slot
        this.menuTitle.onclick = () => {
            // open/close the menu
            this.menu.classList.toggle('closed');
            console.log(this.menu.classList);
        };
    }
}

customElements.define('custom-menu', CustomMenu);
```
{% endtab %}

{% tab title="HTML" %}
```markup
<!-- custom element -->
<custom-menu>
  <!-- ⭐️ <slot> contents from light DOM -->
  <span slot="title">Candy menu</span>
  <li slot="item">Lollipop</li>
  <li slot="item">Fruit Toast</li>
  <li slot="item">Cup Cake</li>
</custom-menu>

<!-- ⭐️ shadow DOM template for `custom-menu` -->
<template id="tmpl">
  <style>
    slot[name="title"] {
      cursor: pointer;
    }

    /* ⭐️ highlight every slotted on hover */
    ::slotted(:hover) {
      background-color: hsla(60, 90%, 50%, 0.8);
      border: 1px dotted black;
      padding: 2px 4px;
      border-radius: 4px;
    }

    .menu.closed ul {
      visibility: hidden;
    }
  </style>
  <div class="menu">
    <!-- ⭐️ named slots -->
    <slot name="title"></slot>
    <ul>
      <slot name="item"></slot>
    </ul>
  </div>
</template>
```
{% endtab %}

{% tab title="codepen" %}
{% embed url="https://codepen.io/lochiwei/pen/LYLxwKp" %}


{% endtab %}
{% endtabs %}

