選單(下拉式)

選單提供一種簡單的方法建立自訂無障礙的下拉式元件,並具有對鍵盤導覽的強健支援。

若要開始,請透過 npm 安裝 Headless UI。

請注意,此函式庫僅支援 Vue 3

npm install @headlessui/vue

選單按鈕是使用 MenuMenuButtonMenuItemsMenuItem 元件建置。

當 「MenuButton」 按下時,將自動開啟/關閉 「MenuItems」 ,當選單開啟時,項目清單會接收焦點,並可自動使用鍵盤導覽。

<template> <Menu> <MenuButton>More</MenuButton> <MenuItems> <MenuItem v-slot="{ active }"> <a :class='{ "bg-blue-500": active }' href="/account-settings"> Account settings </a> </MenuItem> <MenuItem v-slot="{ active }"> <a :class='{ "bg-blue-500": active }' href="/account-settings"> Documentation </a> </MenuItem> <MenuItem disabled> <span class="opacity-75">Invite a friend (coming soon!)</span> </MenuItem> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

Headless UI 會追蹤與每個元件相關的許多狀態,例如目前選擇的列表框選項、視窗是開啟還是關閉,或是目前哪個選單項目使用鍵盤激活。

但是因為元件是無頭且開箱即用完全沒有樣式,所以直到您自行為每個狀態提供所需的樣式,您才無法在 UI 中看到這些資訊。

每個元件都透過 slot-props公開其當前狀態的資訊,您可以使用這些資訊來有條件套用不同樣式或呈現不同內容。

例如, MenuItem 元件公開 active 狀態,告訴您該項目目前是否使用滑鼠或鍵盤對焦。

<template> <Menu> <MenuButton>Options</MenuButton> <MenuItems> <!-- Use the `active` state to conditionally style the active item. --> <MenuItem v-for="link in links" :key="link.href" as="template"
v-slot="{ active }"
>
<a :href="link.href"
:class="{ 'bg-blue-500 text-white': active, 'bg-white text-black': !active }"
>
{{ link.label }} </a> </MenuItem> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' const links = [ { href: '/account-settings', label: 'Account settings' }, { href: '/support', label: 'Support' }, { href: '/license', label: 'License' }, { href: '/sign-out', label: 'Sign out' }, ] </script>

如需所有可用插槽-prop 的完整清單,請參閱 元件 API 文件

每個組件也會透過 `data-headlessui-state` 屬性公開其目前狀態的資訊,您可以使用此屬性有條件地套用不同樣式。

插槽屬性 API 中的任何狀態為 `true` 時,它們會以空格分隔字串的方式列在此屬性中,因此您可以使用 `[attr~=value]` 形式的 CSS 屬性選取器 來鎖定它們。

例如,以下內容顯示 `MenuItems` 組件的一些子 `MenuItem` 組件,在選單開啟且第二個項目為 `active` 時的呈現方式

<!-- Rendered `MenuItems` --> <ul data-headlessui-state="open"> <li data-headlessui-state="">Account settings</li> <li data-headlessui-state="active">Support</li> <li data-headlessui-state="">License</li> </ul>

如果您使用 Tailwind CSS,可以使用 @headlessui/tailwindcss 外掛程式,以使用修改值來鎖定此屬性,例如 `ui-open:*` 和 `ui-active:*`

<template> <Menu> <MenuButton>Options</MenuButton> <MenuItems> <!-- Use the `active` state to conditionally style the active item. --> <MenuItem v-for="link in links" :key="link.href" :href="link.href" as="a"
class="ui-active:bg-blue-500 ui-active:text-white ui-not-active:bg-white ui-not-active:text-black"
>
{{ link.label }} </MenuItem> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' const links = [ { href: '/account-settings', label: 'Account settings' }, { href: '/support', label: 'Support' }, { href: '/license', label: 'License' }, { href: '/sign-out', label: 'Sign out' }, ] </script>

預設情況下,您的 `MenuItems` 執行個體會根據追蹤在 `Menu` 組件內的 `open` 內部狀態自動顯示/隱藏。

<template> <Menu> <MenuButton>More</MenuButton> <!-- By default, the `MenuItems` will automatically show/hide when the `MenuButton` is pressed. --> <MenuItems> <MenuItem><!-- ... --></MenuItem> <!-- ... --> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

如果您想自行處理這件事(可能是因為您需要額外新增一個包裝元素),您可以新增一個 `static` 屬性到 `MenuItems` 執行個體,以告知它始終要呈現,並檢查 `Menu` 提供的 `open` 插槽屬性,以自行控制哪個元素顯示/隱藏。

<template>
<Menu v-slot="{ open }">
<MenuButton>More</MenuButton>
<div v-show="open">
<!-- Using the `static` prop, the `MenuItems` are always rendered and the `open` state is ignored. -->
<MenuItems static>
<MenuItem><!-- ... --></MenuItem> <!-- ... --> </MenuItems> </div> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

選單會預設關閉,但第三方 `Link` 組件可能會使用 `event.preventDefault()`,這會阻止預設行為,因此不會關閉選單。

`Menu` 和 `MenuItem` 公開一個 `close()` 插槽屬性,您可以使用它來命令式關閉選單

<template> <Menu> <MenuButton>Terms</MenuButton> <MenuItems>
<MenuItem v-slot="{ close }">
<MyCustomLink href="/" @click="close">Read and accept</MyCustomLink>
</MenuItem> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' import { MyCustomLink } from './MyCustomLink' </script>

使用 `disabled` 屬性以停用 `MenuItem`。這樣會讓它無法透過鍵盤導覽選擇,且在按下向上/向下箭頭時將會略過它。

<template> <Menu> <MenuButton>More</MenuButton> <MenuItems> <!-- ... --> <!-- This item will be skipped by keyboard navigation. -->
<MenuItem disabled>
<span class="opacity-75">Invite a friend (coming soon!)</span> </MenuItem> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

若要讓選單面板的開啟/關閉動畫化,您可以使用 Vue 內建的 `` 組件。您唯一需要做的事,就是將您的 `MenuItems` 執行個體包在 `` 中,轉場就會自動套用。

<template> <Menu> <MenuButton>More</MenuButton> <!-- Use Vue's built-in `transition` element to add transitions. -->
<transition
enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100"
leave-active-class="transition duration-75 ease-out"
leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0"
>
<MenuItems> <MenuItem><!-- ... --></MenuItem> <!-- ... --> </MenuItems> </transition> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

如果您想調整 Menu 不同子項的多個轉場,請參閱 Headless UI 中包含的轉場元件

role="menu" 的輔助功能語意相當嚴格,且 Menu 的任何非 MenuItem 元件都將自動對輔助技術隱藏,以確保選單按照螢幕閱讀器使用者預期的方式運作。

因此,建議避免呈現 MenuItem 元件以外的任何子項,因為這些內容對使用輔助技術的人來說會無法存取。

如果您想建立具有更彈性內容的下拉式選單,請考慮使用 彈出視窗

預設情況下,Menu 及其子元件各呈現一個對該元件有道理的預設元素。

例如,MenuButton 預設呈現 button,而 MenuItems 呈現 div。相反地,MenuMenuItem 不會呈現元素,而是預設直接呈現其子項。

這可以使用每個元件都存在的 as 屬性來輕鬆變更。

<template> <!-- Render a `div` instead of no wrapper element -->
<Menu as="div">
<MenuButton>More</MenuButton> <!-- Render a `section` instead of a `div` -->
<MenuItems as="section">
<MenuItem v-slot="{ active }"> <a :class='{ "bg-blue-500": active }' href="/account-settings"> Account settings </a> </MenuItem> <!-- ... --> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

若要指示元素直接呈現其子項,而不使用包裹元素,請使用 as="template"

<template> <Menu> <!-- Render no wrapper, instead pass in a `button` manually. -->
<MenuButton as="template">
<button>More</button> </MenuButton> <MenuItems> <MenuItem v-slot="{ active }"> <a :class='{ "bg-blue-500": active }' href="/account-settings"> Account settings </a> </MenuItem> <!-- ... --> </MenuItems> </Menu> </template> <script setup> import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' </script>

如果您在 MenuItem 內使用互動元素,例如 <a> 標籤,這一點很重要。如果 MenuItemas="div",則 Headless UI 提供的屬性會轉發到 div,而不是 a,這表示您無法再透過鍵盤前往 <a> 標籤提供的 URL。

按一下 MenuButton 會切換選單並將焦點放在 MenuItems 元件。焦點會被困在開啟的選單中,直到按下 Escape 或使用者按一下選單外。關閉選單會將焦點返回至 MenuButton

按一下 MenuButton 會切換選單。按一下開啟選單外的任何地方都會關閉該選單。

命令說明

EnterSpaceMenuButton 取得焦點時

開啟選單並將焦點置於第一個未停用的項目

ArrowDownArrowUpMenuButton 取得焦點時

開啟選單並將焦點置於第一個/最後一個未停用的項目

Esc 當選單開啟時

關閉所有開啟的選單

ArrowDownArrowUp當選單開啟時

將焦點置於上一個/下一個未停用的項目

HomePageUp 當選單開啟時

將焦點置於第一個未停用的項目

EndPageDown 當選單開啟時

將焦點置於最後一個未停用的項目

EnterSpace 當選單開啟時

啟用/點擊目前的選單項目

A–Za–z 當選單開啟時

將焦點置於與鍵盤輸入相符的第一個項目

所有相關的 ARIA 屬性都會自動進行管理。

如需了解在 Menu 中實作的所有輔助功能的完整指南,請參閱 ARIA 選單按鈕指南

選單最適合用於與在多數作業系統標題列中會看到的選單類似之使用者介面元素。這些有特定可近性語意,且其內容應限於連結或按鈕清單。焦點會受限於開啟的選單中,因此你無法於內容或選單之外使用「Tab」鍵。箭頭鍵會用來於「選單」項目中導覽。

以下是在何時可以使用 Headless UI 中的類似元件

  • <Popover />。彈出選單是通用的浮動選單。它們會於觸發它們的按鈕附近出現,而你可以於其中放置任意標記,例如圖片或不可點擊的內容。「Tab」鍵會像導覽其他一般標記一樣導覽彈出選單的內容。它們很適合用於建立具備可擴充內容和彈出式面板的標頭 nav 項目。

  • <Disclosure />。揭露對用於擴充以顯示附加資訊的元素很有用,例如可切換的常見問答區段。它們通常會內嵌呈現,並於顯示或隱藏時重新整理文件。

  • <Dialog />。對話方塊用於抓住使用者的注意力。它們通常會於螢幕中央呈現浮動面板,並使用背景來減少應用程式其餘內容。它們也會擷取焦點,並防止在對話方塊被關閉前離開對話方塊的內容。

屬性預設值說明
as範本
字串 | 元件

Menu 應呈現的元素或元件。

插槽屬性說明
open

布林

選單是否開啟。

close

() => void

關閉選單,並重新聚焦 MenuButton

屬性預設值說明
asbutton
字串 | 元件

MenuButton 應呈現的元素或元件。

插槽屬性說明
open

布林

選單是否開啟。

屬性預設值說明
asdiv
字串 | 元件

MenuItems 應呈現的元素或元件。

staticfalse
布林

元素是否應忽略內部管理的開啟/關閉狀態。

注意:無法同時使用 staticunmount。如果你嘗試這樣做,將會出現 TypeScript 錯誤。

unmounttrue
布林

元素是否應根據已開啟/已關閉狀態取消掛載或隱藏。

注意:無法同時使用 staticunmount。如果你嘗試這樣做,將會出現 TypeScript 錯誤。

插槽屬性說明
open

布林

選單是否開啟。

屬性預設值說明
as範本
字串 | 元件

要由 MenuItem 呈現的元素或元件。

停用false
布林

這個項目是否應該停用,以進行鍵盤導覽及符合 ARIA 用途。

插槽屬性說明
主動

布林

這個項目是否為清單中主動/焦點項目。

停用

布林

這個項目是否停用,以進行鍵盤導覽及符合 ARIA 用途。

close

() => void

關閉選單,並重新聚焦 MenuButton

如果您有興趣使用 Headless UI 和 Tailwind CSS 的預先設計的元件範例,請查看 Tailwind UI — 我們建立的一系列設計精美且製作精良的元件。

這是支持我們處理這類開源專案計畫的絕佳方式,能讓我們改善專案並維持完善的品質。