這系列無障礙的鐵人賽文章,實踐的內容主要是根據 W3C:WAI-ARIA 的實踐,從設計模式及組件(Design Patterns and Widgets)裡面挑選最想嘗試的,如果有朋友想瞭解全部 Widget 該怎麼實作及其規範,歡迎自行爬規範內容,也許我們可以討論一下;若以下文章內容理解有任何錯誤,請多指教~
3.17 Radio Group
A radio group is a set of checkable buttons, known as radio buttons, where no more than one of the buttons can be checked at a time. Some implementations may initialize the set with all buttons in the unchecked state in order to force the user to check one of the buttons before moving past a certain point in the workflow.
回想到剛開始學習網頁的時候,常常把「checkbox」和「radio」搞不清楚,這兩個在原生表單元素中都是很常使用到的。到底為什麼 radio 要叫做 radio 呢?
(圖片來源:MDN)
因為 radio 的設計來說,不論外觀或是功能,都和早期的收音機上的按鈕非常相似,一次只能選擇一個功能,比如說上圖的
LW
,那麼點按其他功能BC-A
時,LW
就會跳起來,只運作你最後選擇的BC-A
,也就是「單選」的意思。
我們來看一下原生的表單元素 radio 的寫法:
<input type="radio" id="ramen" name="japanese-food" value="ramen" checked>
<input>
標籤,類型是 radio
。id
,值是 remen
(拉麵)。name
和 checked
非常重要,之後會舉例說明。value
是當前那個 radio
的值,是我們會傳給後端的內容,而它並不會出現在 UI 畫面上,如下圖左邊的呈現,使用者點的時候並不會知道他點了什麼東西(黑人問號???),下圖右邊的畫面才是真正使用者比較適合看到的內容,知其所以。Q:那麼我們怎麼做,才可以呈現右邊的文字補充呢?
<label>
來加上標籤,必須(握拳!)原生寫法會透過 <label>
來為每個表單元素加上標註,螢幕閱讀器也能念出到每個 input
的「標題」,加上標題後,當我們在表單中點擊了「那個標題」,就等於在點擊以建立關聯性的 input
。如果你已經知道的話,那麼你知道建立關聯性有兩種做法嗎?
<label>
和 <input>
拆開來寫,用 for
屬性指向 input
的 id
。
<label>
當作容器把 <input>
包起來,不用使用 for
與 id
囉!因為連結性已隱含在其中。
如果想了解更多 label 的無障礙做法,可以來看 WAI 的教學,這裡不再贅述。
那麼來說明一下 name 與 checked 係蝦咪?
(圖片來源:Unsplash)
今天想點套餐來吃個!
主餐的部分要從 Menu 中選出一項主食,假設你喜歡吃拉麵好了,拉麵區有「醬油拉麵、蔥燒拉麵、海鮮拉麵....等」,到底要吃哪個比較好呢?
那麼這樣的情境在 radio 的呈現就會是一組的選項,我們會將它叫做「Radio Group」。
原始碼長這樣:
<!-- 三個 radio 組成的 radio group -->
<label>
<input type="radio" name="main-dish" value="1" checked>
<p>醬油拉麵</p>
</label>
<label>
<input type="radio" name="main-dish" value="2" checked>
<p>蔥燒拉麵</p>
</label>
<label>
<input type="radio" name="main-dish" value="3" checked>
<p>海鮮拉麵</p>
</label>
radio
組成的 group
,原因是它們的 name
寫的值都是同一個欄位 main-dish
。
Radio Gruup
)、主食(name="main-dish"
)。checked
是可加、可不加的狀態,在一個 radio group
中的 radio
加上 checked
表示這組三個選項當中,我們現在單選的是它。
value
是我們跟後端溝通好要儲存的值,和我們顯示的 UI 名稱不一定要一樣。
main-dish
這個欄位會傳值 1
給後端,而「醬油拉麵」只是個給使用者標題。radio
組成,一組只會有一個 radio
是 checked
的狀態。上面介紹完 radio
的定義及原生的作法,以下內容要說明 Radio Group
作為 WAI 中的設計模式之一,在無障礙中有什麼要注意的地方?如果今天要自己刻一個同性質的做法,也就是使用非原生 input ,那麼一定要特別特別注意無障礙的實現。
radio
是處於 checked
的狀態嗎?
focus
在該 radio
上。focus
在組內第一個 radio
上。radio
的選取狀態( checked
)。radio
」 ,如果是最後一個,就移動到組中「第一個 radio
」,並選中新焦點的 radio
。radio
」 ,如果是第一個,就移動到組中「最後一個 radio
」,並選中新焦點的 radio
。role
,值為 radiogroup
。radio
不是原生的 input
,那麼我們 radio
需要加上角色 role="radio"
。radio
都需要加上 aria-checked
,值是 true
或 false
。radio
加上角色 role="radio"
之後,每個 radio
會以本身中的文字內容作為「名稱」標示(label),不過也可以使用 aria-labelledby="某元素id"
或是 aria-label="醬油拉麵"
來補充語義。aria-labelledby="某元素id"
或是 aria-label="主餐"
來補充語義。今天一樣引用 WAI-ARIA Practice 1.1 的範例 Radio Group Example Using aria-activedescendant,改成我們自己想要的情境吧!
好,你剛剛去日式料理餐廳用餐完畢之後,服務人員想請你填寫「服務滿意度調查」,像這樣的介面的無障礙該怎麼實現呢?
希望能做到:
<!-- Radio Gruop 的標題 -->
<h3 id="group_label_1">
環境舒適嗎?
</h3>
<!-- Radio Gruop 的主體 -->
<ul id="rg1"
class="radiogroup"
role="radiogroup"
aria-labelledby="group_label_1"
aria-activedescendant="rb11"
tabindex="0">
<li id="rb11"
role="radio"
aria-label="極好"
aria-checked="false">
</li>
<li id="rb12"
role="radio"
aria-label="還行"
aria-checked="false">
</li>
<li id="rb13"
role="radio"
aria-label="普通"
aria-checked="false">
</li>
<li id="rb14"
role="radio"
aria-label="不喜歡"
aria-checked="false">
</li>
<li id="rb15"
role="radio"
aria-label="討厭"
aria-checked="false">
</li>
</ul>
<ul>
作為 Radio Group 的主體, <li>
是 radio
。<ul>
:Radio Group 的主體!
role="radiogroup"
,定義角色是 radio group。aria-labelledby
填入標題的元素 id
,上面例子當中就是那個 <h3>
標籤。aria-activedescendant
中的值,代表這整個 Radio Group 選中誰。tabindex="0"
,讓鍵盤可以對一個非操控元素 <ul>
產生焦點(focusable)。<li>
:每一個 li 都當作 radio。
role="radio"
,定義角色是 radio
。id
的值,是我們要傳給 <ul>
中的 aria-activedescendant
屬性的值。aria-label
是我們賦予的標題,螢幕閱讀器會唸出這個內容給使用者聽,他們將會知道目前的 radio
是什麼選項內容。aria-checked
可以設定現在是否被選擇,記得,同一組 Radio Gruop 中,只會有一個選項被選取,是單選的唷!花了我九牛二虎之力,跟設計圖一樣的完整程式碼 => codepen 在這裏,快來使用鍵盤操作看看吧!