切換開關

切換開關提供一個愉悅的介面讓您在兩個狀態間切換一個值,而且提供了與原生核取方塊元素一樣的語意和鍵盤導覽。

要開始使用,請透過 npm 安裝 Headless UI

npm install @headlessui/react

切換器是以 Switch 元件建構的。您可以直接點選元件,或是在元件取得焦點時按空白鍵來切換。

切換切換器會呼叫 onChange 函式,並提供 checked 值的否定版本。

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
      checked={enabled}
      onChange={setEnabled}
      className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600"
    >
      <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" />
    </Switch>
  )
}

Headless UI 會追蹤每個元件的大量狀態,例如切換器是否已選取、快顯視窗是否已開啟或關閉,或選單中哪個項目目前已使用鍵盤取得焦點。

但由於元件是 headless,而且預設完全沒有樣式,因此除非您自行提供每個狀態需要的樣式,否則無法在使用者介面中看到這些資訊。

要為 Headless UI 元件的不同狀態套用樣式的最簡單方法,就是使用每個元件提供的 data-* 屬性。

例如,Switch 元件提供 data-checked 屬性,告訴您切換器目前是否已選取,以及 data-disabled 屬性,告訴您切換器目前是否已停用。

<!-- Rendered `Switch` -->
<button data-checked data-disabled>
  <!-- ... -->
</button>

使用 CSS 屬性選擇器,依據這些資料屬性的存在來有條件套用樣式。如果您使用 Tailwind CSS,可以使用 資料屬性修改器 輕鬆完成

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
      checked={enabled}
      onChange={setEnabled}
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 data-[checked]:bg-blue-600 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
>
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" />
</Switch> ) }

請參閱 元件 API,以取得所有可用資料屬性的清單。

每個元件也會透過 渲染屬性 提供其目前狀態的資訊,您可以用這些屬性來有條件套用不同樣式或渲染不同內容。

例如,Switch 元件會提供 checked 狀態,告訴您切換器目前是否已選取,以及 disabled 狀態,告訴您切換器目前是否已停用。

import { Switch } from '@headlessui/react'
import clsx from 'clsx'
import { Fragment, useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch checked={enabled} onChange={setEnabled} as={Fragment}>
{({ checked, disabled }) => (
<button className={clsx( 'group inline-flex h-6 w-11 items-center rounded-full',
checked ? 'bg-blue-600' : 'bg-gray-200',
disabled && 'cursor-not-allowed opacity-50'
)}
>
<span className="sr-only">Enable notifications</span> <span
className={clsx('size-4 rounded-full bg-white transition', checked ? 'translate-x-6' : 'translate-x-1')}
/>
</button>
)}
</Switch> ) }

請參閱 元件 API,以取得所有可用渲染屬性的清單。

使用 Field 元件包裝 LabelSwitch,以使用已產生的 ID 自動關聯它們

import { Field, Label, Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
<Field>
<Label>Enable notifications</Label>
<Switch checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" > <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch>
</Field>
) }

預設情況下,按一下 Label 會切換 Switch,就像標籤對應原生 HTML 核取方塊一樣。如果你想將 Label 做為不可按鈕,可以將 passive 道具加入 Label 元件中

<Label passive>Enable beta features</Label>

Field 內使用 Description 元件,並使用 aria-describedby 屬性自動關聯它與 Switch

import { Description, Field, Label, Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
<Field>
<Label>Enable notifications</Label>
<Description>Get notified about important changes in your projects.</Description>
<Switch checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" > <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch>
</Field>
) }

disabled 道具加入 Field 元件以停用 Switch 及其相關聯的 LabelDescription

import { Description, Field, Label, Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
<Field disabled>
<Label className="data-[disabled]:opacity-50">Enable notifications</Label> <Description className="data-[disabled]:opacity-50"> Get notified about important changes in your projects. </Description> <Switch checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50" > <span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> </Field> ) }

你也可以在 Field 外部停用開關,將 disabled 道具直接加入 Switch 本身即可。

如果你將 name 道具加入 Switch,系統會產生一個隱藏的 input 元素,並與開關狀態同步。

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <form action="/accounts" method="post">
      <Switch
        checked={enabled}
        onChange={setEnabled}
name="terms-of-service"
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> <button>Submit</button> </form> ) }

這讓你可以在原生 HTML <form> 中使用開關,並像你的開關為原生 HTML 表單控制項一樣執行傳統的表單提交。

預設情況下,當開關已勾選時,值會是 on;當開關未勾選時,則不會顯示值。

<!-- Rendered hidden input -->
<input type="hidden" name="terms-of-service" value="on" />

如果你需要,可以使用 value 道具自訂值

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <form action="/accounts" method="post">
      <Switch
        checked={enabled}
        onChange={setEnabled}
        name="terms-of-service"
value="accept"
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> <button>Submit</button> </form> ) }

當開關已勾選時,隱藏的輸入欄位會使用你的自訂值

<!-- Rendered hidden input -->
<input type="hidden" name="terms-of-service" value="accept" />

像字串等基本值會產生一個包含該值的單一隱藏輸入欄位,但物件等複雜的值則會使用方括號表示法編碼至多個輸入欄位中,中括號用於表示名稱。

如果你省略 checked 道具,Headless UI 會在內部追蹤其狀態,讓你用它作為 非受控元件

處於非受控狀態時,你可以使用 defaultChecked 道具預設勾選 Switch

import { Switch } from '@headlessui/react'

function Example() {
  return (
    <form action="/accounts" method="post">
      <Switch
defaultChecked={true}
name="terms-of-service" className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> <button>Submit</button> </form> ) }

當使用具備 HTML 表單的 切換,或搭配利用 FormData 收集狀態的表單 API,而不是使用 React 狀態追蹤時,這可以簡化您的程式碼。

您提供的任何 onChange 屬性仍會在元件值變更時被呼叫,以防您需要執行任何副作用,但您不需要使用它來自己追蹤元件的狀態。

因為切換通常總是會呈現在 DOM 中(而不是像其他元件一樣進行掛載或卸載),所以簡單的 CSS 過渡通常足以讓您的切換動畫。

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
      checked={enabled}
      onChange={setEnabled}
className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600"
>
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" />
</Switch> ) }

由於沒辦法呈示任何畫面,Headless UI 元件也能與 React 生態系統中的其他動畫函式庫相容,例如 Framer MotionReact Spring

預設情況下,Switch 元件會呈現為一個 button

使用 as 屬性來將元件呈現為不同的元素或您自己的自訂元件,並確保您的自訂元件 傳遞轉發參照,以便 Headless UI 能正確連接所有內容。

import { Switch } from '@headlessui/react'
import { useState } from 'react'

function Example() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch
as="div"
checked={enabled} onChange={setEnabled} className="group inline-flex h-6 w-11 items-center rounded-full bg-gray-200 transition data-[checked]:bg-blue-600" >
<span className="size-4 translate-x-1 rounded-full bg-white transition group-data-[checked]:translate-x-6" /> </Switch> ) }

指令說明

空白鍵Switch 有焦點時

切換開關

Enter在表單中時

提交表單

主要的切換元件。

屬性預設值說明
as按鈕
字串 | 元件

切換應該呈現為何種元件。

checked
布林值

切換是否被勾選。

defaultChecked
T

用作非受控元件時的預設勾選值。

onChange
(value: Boolean) => void

切換被切換時呼叫的函式。

name
字串

在表單中使用應該呈現為時使用的名稱。

表單
字串

所屬應該呈現為的表單 ID。

如果提供了 name 但未提供 form,則應該呈現為會將其狀態新增到最接近的祖先 form 元素。

value
字串

如果切換此元件,則會在表單內使用此值。

資料屬性顯示 Prop說明
data-checkedchecked

布林值

是否應該呈現為處於已選取狀態。

data-disableddisabled

布林值

是否應該呈現為處於已停用狀態。

data-focusfocus

布林值

是否應該呈現為處於焦點狀態。

data-hoverhover

布林值

是否應該呈現為處於滑鼠懸停狀態。

data-activeactive

布林值

是否應該呈現為處於「按鈕被按下」的活躍狀態。

data-autofocusautofocus

布林值

是否已將 autoFocus 屬性設定為 true

data-changingchanging

布林值

是否正在切換已選取狀態。

checked 狀態發生變化時,changing 會在兩個動畫畫格中保持為 true,讓你可以微調過場動畫。

標籤說明與表單控制項分組在一起。

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

切換field何種元件。

disabledfalse
布林值

欄位是否已停用。

資料屬性顯示 Prop說明
data-disableddisabled

布林值

欄位是否已停用。

標籤元件會標記表單控制項。

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

切換label何種元件。

passivefalse
布林值

如果為 true,則點選標籤時不會讓表單控制項獲得焦點。

資料屬性顯示 Prop說明
data-disableddisabled

布林值

父層 欄位 是否已停用。

說明元件會說明表單控制項。

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

切換description何種元件。

資料屬性顯示 Prop說明
data-disableddisabled

布林值

父層 欄位 是否已停用。

如果你有興趣瞭解使用 Headless UI 來建立預先設計的 Tailwind CSS 開關和切換範例> 請查看Tailwind UI,這是我們建立的一系列美麗設計及精心打造的元件。

這是支持我們進行此類開源計畫的絕佳方法,讓我們有能力改善計畫內容並讓計畫內容維持良好的狀態。