コンテンツにスキップ

ui:dropdown

ボタンクリックで開くDrop Down Menuコンポーネントです。

  • Web Component (<ui-dropdown>) として提供しています。
  • WAI-ARIA Authoring Practices の Menu Button Pattern に準拠しています。
Source Code
<button type="button" data-ui-dropdown-trigger="basic-menu">操作</button>
<ui-dropdown class="ui:dropdown" id="basic-menu" aria-label="操作メニュー">
<button class="ui:dropdown__item" type="button">編集</button>
<button class="ui:dropdown__item" type="button">複製</button>
<button class="ui:dropdown__item" type="button" disabled>Disabled</button>
</ui-dropdown>

リンク (<a href>) も項目として使えます。

Source Code
<button type="button" data-ui-dropdown-trigger="nav-menu">メニュー</button>
<ui-dropdown class="ui:dropdown" id="nav-menu" aria-label="ナビゲーション">
<a class="ui:dropdown__item" href="/">ホーム</a>
<a class="ui:dropdown__item" href="/about">概要</a>
<hr class="ui:separator" />
<a class="ui:dropdown__item" href="https://example.com" target="_blank">
外部リンク<ui-icon data-icon="external-link" aria-label="別ウィンドウで開きます"></ui-icon>
</a>
</ui-dropdown>
属性 / CSS変数要素効果
disabledbutton.ui:dropdown__item-項目を無効化(キーボード移動からスキップ)
aria-disabled.ui:dropdown__item"true"項目を無効化(button / a 両対応)
data-value.ui:dropdown__itemstringui-dropdown:select カスタムイベントの detail.value で取得可能
--ui-dropdown-min-widthrootCSS <length>メニュー最小幅。既定 12rem
属性要素効果
data-ui-dropdown-trigger任意対象 dropdown の "id"自動でA11y属性が付与され、イベントハンドラが登録されます

トリガーボタン上(閉じている状態)

Section titled “トリガーボタン上(閉じている状態)”
キー動作
Enter Enter / Space Space メニューを開く + 最初の項目へフォーカス
Down Arrow Down Arrow メニューを開く + 最初の項目へフォーカス
Up Arrow Up Arrow メニューを開く + 最後の項目へフォーカス
キー動作
Down Arrow Down Arrow / Up Arrow Up Arrow 次 / 前の有効な項目へフォーカス(ループ)
Home Home 最初の項目へ
End End 最後の項目へ
Enter Enter / Space Space フォーカス中の項目を activate(= click)して close
Tab Tab メニューを閉じる(focus はそのまま自然遷移)
Escape Escape メニューを閉じる(popover ネイティブ)
backdrop クリックメニューを閉じる(popover ネイティブ)

JS が trigger 要素の getBoundingClientRect() を見て、メニューの位置を計算します:

  • 既定: trigger の 直下、左端揃え
  • viewport の 下に入らない 場合は 上に flip (上に入る場合のみ)
  • viewport の 右にはみ出す 場合は左方向に shift
  • popover は top layer (= viewport 座標) なので position: fixed で配置

<ui-dropdown> は Light DOM の Web Component ですが、ユーザーアクションによって表示される性質上、JS upgrade 前は 完全に非表示 にしています。

<!-- トリガー (どこに置いても OK) -->
<button type="button" data-ui-dropdown-trigger="my-menu">操作</button>
<!-- メニュー本体 -->
<ui-dropdown class="ui:dropdown" id="my-menu" aria-label="操作メニュー">
<button class="ui:dropdown__item" type="button">編集</button>
<a class="ui:dropdown__item" href="/settings">設定</a>
<hr class="ui:dropdown__separator" />
<button class="ui:dropdown__item" type="button" disabled>共有 (準備中)</button>
<button class="ui:dropdown__item" type="button" data-variant="danger">削除</button>
</ui-dropdown>
要素役割
<ui-dropdown id>メニュー本体。role="menu" が自動付与される
.ui:dropdown__item (<button> / <a>)メニュー項目。role="menuitem" が自動付与される
.ui:dropdown__separator (<hr>)区切り線。ネイティブ role="separator" が効く
import type { UiDropdownElement } from "@packages/ui/dropdown";
const $menu = document.querySelector<UiDropdownElement>("#my-menu");
$menu?.open(); // プログラム open
$menu?.open(triggerEl); // 特定 trigger 起点で open (位置計算で使われる)
$menu?.close();
console.log($menu?.isOpen);
イベント発火元cancelablebubblesdetail
ui-dropdown:beforeopen<ui-dropdown>{ $trigger: HTMLElement | null }
ui-dropdown:open<ui-dropdown>{ $trigger: HTMLElement | null }
ui-dropdown:close<ui-dropdown>{}
ui-dropdown:select<ui-dropdown>{ $item: HTMLElement; value: string | null }
import type { DropdownOpenDetail, DropdownSelectDetail } from "@packages/ui/dropdown";
// 項目選択
document.addEventListener("ui-dropdown:select", (e) => {
const { $item, value } = e.detail;
console.log("selected:", value, $item);
});
// open をキャンセル (例: 未保存変更時)
document.addEventListener("ui-dropdown:beforeopen", (e) => {
if (someValidation()) {
e.preventDefault();
}
});