Dropdown

A menu that opens on click and closes on selection or an outside click. The outside-click handler is the sanctioned document-level listener: added in onCreate, removed in onDestroy.

Markup

<button @click="toggle" data-bind="aria-expanded:open ? 'true' : 'false'" aria-haspopup="menu">
  Options <span>v</span>
</button>

<div role="menu" data-show="open">
  <template data-each="items" data-key="id">
    <button role="menuitem" @click="choose" data-bind="data-label:item.label" data-text="item.label"></button>
  </template>
</div>

<p>Selected: <span data-text="selected || 'none'"></span></p>

Component

Micra.define("dropdown", {
  state: {
    open: false,
    selected: "",
    items: [
      { id: 1, label: "Rename" },
      { id: 2, label: "Duplicate" },
      { id: 3, label: "Archive" },
      { id: 4, label: "Delete" },
    ],
  },
  toggle() {
    this.state.open = !this.state.open;
  },
  choose(e) {
    this.state.selected = e.currentTarget.dataset.label;
    this.state.open = false;
  },
  onCreate() {
    this._outside = (e) => {
      if (!this.$el.contains(e.target)) this.state.open = false;
    };
    document.addEventListener("click", this._outside);
  },
  onDestroy() {
    document.removeEventListener("click", this._outside);
  },
});

this.$el is the component root, so contains cleanly distinguishes clicks inside the menu from clicks anywhere else on the page.