概略說明如何建構回應式、適應性且無障礙功能,並提供多選元件,以便排序及篩選使用者體驗。
在這篇文章中,我想分享如何建立複選元件的想法。立即試用示範。
如果你偏好使用影片,也可以觀看這篇 YouTube 文章:
總覽
使用者經常會看到項目,有時是「許多」項目,這時建議您提供減少清單的方式,避免選項超載。這篇網誌文章將探討篩選 UI 來減少選擇的方法。其運作方式為顯示使用者可以選取或取消選取的項目屬性,進而減少結果,進而減少選項超載。
互動
目標是為所有使用者及其各種輸入類型啟用迅速遍歷選項。您可以透過可彈性調整、回應及回應的元件組合來達成。適用於電腦、鍵盤和螢幕閱讀器的傳統核取方塊,以及觸控使用者適用的 <select
multiple>
。
因此,我們決定使用內建的多選功能 (而非電腦) 儲存工作和建立工作,但我認為,比起在單一元件中建構完整的回應式體驗,我相信能夠以更少的程式碼債務打造合適的體驗。
觸控
觸控元件可節省空間,並協助行動裝置使用者的互動準確度。這項功能會將整個核取方塊側欄收合至 <select>
內建重疊觸控體驗,藉此節省空間。它可以顯示系統提供的大型觸控重疊體驗,協助輸入準確度。
鍵盤和遊戲手把
以下示範如何在鍵盤中使用 <select multiple>
。
這個內建複選功能無法設定樣式,且僅供用來顯示許多選項的精簡版面配置。您看不出那個小盒子裡 無法看到這麼多選項的呢?雖然您可以變更大小,但目前仍無法當做核取方塊側欄使用。
標記
這兩個元件會放在同一個 <form>
元素中。系統會觀察這份表單的結果 (無論是核取方塊或複選題),並用來篩選格線,但也可以提交給伺服器。
<form>
</form>
核取方塊元件
一組核取方塊應納入 <fieldset>
元素中,並給予 <legend>
。當 HTML 以這種方式建構時,螢幕閱讀器和 FormData 會自動瞭解元素的關係。
<form>
<fieldset>
<legend>New</legend>
… checkboxes …
</fieldset>
</form>
分組設定完成後,即可為每個篩選器新增 <label>
和 <input type="checkbox">
。我選擇在 <div>
中納入我的範圍,這樣 CSS gap
屬性就能在標籤進入多行時維持其間距,並保持對齊。
<form>
<fieldset>
<legend>New</legend>
<div>
<input type="checkbox" id="last 30 days" name="new" value="last 30 days">
<label for="last 30 days">Last 30 Days</label>
</div>
<div>
<input type="checkbox" id="last 6 months" name="new" value="last 6 months">
<label for="last 6 months">Last 6 Months</label>
</div>
</fieldset>
</form>
<select multiple>
元件
<select>
元素的大部分特徵為 multiple
。將屬性與 <select>
元素搭配使用時,使用者可以從清單中選擇多項。就像將互動從圓形按鈕變更為核取方塊清單一樣。
<form>
<select multiple="true" title="Filter results by category">
…
</select>
</form>
如要在 <select>
內為群組加上標籤及建立群組,請使用 <optgroup>
元素,並為其提供 label
屬性和值。這個元素和屬性值類似於 <fieldset>
和 <legend>
元素。
<form>
<select multiple="true" title="Filter results by category">
<optgroup label="New">
…
</optgroup>
</select>
</form>
接著為篩選器新增 <option>
元素。
<form>
<select multiple="true" title="Filter results by category">
<optgroup label="New">
<option value="last 30 days">Last 30 Days</option>
<option value="last 6 months">Last 6 Months</option>
</optgroup>
</select>
</form>
使用計數器追蹤輸入內容,以便將輔助技術資料納入考量
此使用者體驗會使用狀態角色技巧,追蹤及維護螢幕閱讀器和其他輔助技術的篩選器記錄數據。YouTube 影片會示範這項功能整合開頭為 HTML 和 role="status"
屬性。
<div role="status" class="sr-only" id="applied-filters"></div>
這個元素會朗讀變更內容。當使用者與核取方塊互動時,我們可以更新 CSS 計數器的內容。為此,我們必須先建立計數器,並為輸入和狀態元素的父項元素命名。
aside {
counter-reset: filters;
}
根據預設,計數為 0
,這非常好,因此這項設計預設沒有任何 :checked
。
接著,為增加新建的計數器,我們會指定 :checked
中 <aside>
元素的子項。當使用者變更輸入狀態時,filters
計數器會計算。
aside :checked {
counter-increment: filters;
}
CSS 現已知道核取方塊 UI 的一般數據,且狀態角色元素為空白,正在等候值。由於 CSS 會維護記憶體中的計量,counter()
函式可讓您從虛擬元素內容存取值:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
狀態角色元素的 HTML 現在會對螢幕閱讀器朗讀「2 個篩選條件」。這是很好的開始,但我們可以做得更好,例如分享篩選器更新後的結果數。我們會在 JavaScript 執行這項作業,因為這是計數器不支援的功能。
加入巢狀結構
計數器演算法非常適合 CSS 巢狀結構 1,因為我可以將所有邏輯放入同一個區塊。易於讀取及更新。
aside {
counter-reset: filters;
& :checked {
counter-increment: filters;
}
& #applied-filters::before {
content: counter(filters) " filters ";
}
}
版面配置
本節說明兩個元件之間的版面配置。大多數的版面配置樣式都適用於電腦核取方塊元件。
表單
為了提供最佳的易讀性和可讀性,表單的寬度上限為 30 個字元,主要為每個篩選器標籤設定光學線寬度。這個表單使用格線版面配置和 gap
屬性,將欄位間距留白。
form {
display: grid;
gap: 2ch;
max-inline-size: 30ch;
}
<select>
元素
標籤和核取方塊清單在行動裝置上佔用太多空間。 因此,版面配置會檢查使用者的主要指向裝置,變更觸控體驗。
@media (pointer: coarse) {
select[multiple] {
display: block;
}
}
coarse
值表示使用者無法透過主要輸入裝置,以高精確度與螢幕互動。在行動裝置上,指標值通常是 coarse
,因為這是主要互動。在電腦裝置上,指標值通常為 fine
,因為通常會連接滑鼠或其他高精確度輸入裝置。
田野
具有 <legend>
的 <fieldset>
預設樣式和版面配置並不相同:
一般而言,為了維持子項元素的空間,我會使用 gap
屬性,但 <legend>
的獨特位置會導致要建立一組均等距離的子項。使用相鄰的同層級選取器和 margin-block-start
,而不是 gap
。
fieldset {
padding: 2ch;
& > div + div {
margin-block-start: 2ch;
}
}
這樣做會略過 <legend>
,使其僅指定 <div>
子項來調整大小空間。
篩選器標籤和核取方塊
標籤文字如果太長,可能會是 <fieldset>
的直接子項,且不超過表單 30ch
的寬度上限。「自動換行」是不錯的做法,但文字和核取方塊之間沒有對齊Flexbox 是相當理想的選擇。
fieldset > div {
display: flex;
gap: 2ch;
align-items: baseline;
}
![這張螢幕截圖顯示在多行包裝情境中,勾號與第一行文字對齊的方式。](https://web.dev/static/articles/building/a-multi-select-component/image/screenshot-showing-the-c-b135285ef7d8a.png?authuser=7&hl=zh-tw)
動畫格線
版面配置動畫是由 Isotope 完成。效能強大、功能強大的外掛程式,適用於互動排序及篩選。
JavaScript
除了協助自動化調度管理簡潔的動畫互動格狀外,JavaScript 還可用來修飾幾個粗糙的邊緣。
將使用者輸入內容正規化
此設計有一個表單提供兩種不同的輸入方式,且兩者並未序列化。但我們可以利用某些 JavaScript 將資料正規化。
我選擇將 <select>
元素資料結構與已分組的核取方塊結構保持一致。為此,系統會將 input
事件監聽器新增至 <select>
元素,該元素會對應 selectedOptions
。
document.querySelector('select').addEventListener('input', event => {
// make selectedOptions iterable then reduce a new array object
let selectData = Array.from(event.target.selectedOptions).reduce((data, opt) => {
// parent optgroup label and option value are added to the reduce aggregator
data.push([opt.parentElement.label.toLowerCase(), opt.value])
return data
}, [])
})
現在您就可以放心提交表單,以示範來說,就是指示 Isotope 控制篩選依據。
完成狀態角色元素
元素只會根據核取方塊互動計算及朗讀篩選器計數,但我認為最好另外分享結果的數量,並確保系統也納入了 <select>
元素選項的計算結果。
counter()
中已反映 <select>
個元素選項
在資料正規化專區中,系統已在輸入時建立事件監聽器。這個函式結束時,會得知所選篩選器的數量和這些篩選器的結果數量。這些值可以傳遞到如下所示的狀態角色元素。
let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length
role="status"
元素反映的結果
:checked
內建可將所選篩選器數量傳遞至狀態角色元素的方法,但無法查看已篩選的結果數量。JavaScript 會留意與核取方塊互動的情形,並在篩選網格後新增 textContent
,如同 <select>
元素一樣。
document
.querySelector('aside form')
.addEventListener('input', e => {
// isotope demo code
let filterResults = IsotopeGrid.getFilteredItemElements().length
document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})
此工作同時完成了「2 個濾鏡,產生 25 筆結果」。
現在,我們擁有優異的輔助技術,無論使用者如何進行互動,都能獲得優異的輔助技術體驗。
結論
現在既然你已經知道我怎麼做,你會怎麼做‽ 🙂?
讓我們帶您更多元的方法,並瞭解運用網路打造網站的所有方式。 請建立示範並透過 Twitter 推文連結,我就能將這項工具新增至下方的「社群重混」部分!
社群重混作品
這裡還沒有內容!