基礎概略說明如何建構可回應、自動調整且無障礙的多重選取元件,以提供排序和篩選使用者體驗。
在這篇文章中,我想分享如何建構多重選項元件的想法。歡迎前往示範操作。
如果您喜歡看影片,請參考這篇文章的 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
數量。
接下來,為了增加新建立的計數器,我們會將 <aside>
元素的子項設為 :checked
。當使用者變更輸入狀態時,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
屬性,為 fieldset 留出間距。
form {
display: grid;
gap: 2ch;
max-inline-size: 30ch;
}
<select>
元素
標籤清單和核取方塊在行動裝置上都會占用太多空間。因此,版面配置會檢查使用者的主要指標裝置,以便變更觸控體驗。
@media (pointer: coarse) {
select[multiple] {
display: block;
}
}
如果值為 coarse
,表示使用者無法透過主要輸入裝置,以高精確度與螢幕互動。在行動裝置上,由於主要互動方式是觸控,因此指標值通常是 coarse
。在電腦裝置上,由於通常會連接滑鼠或其他高精確度輸入裝置,因此指標值通常為 fine
。
設定範圍
<fieldset>
包含 <legend>
的預設樣式和版面配置如下:
一般而言,為了為子項元素預留空間,我會使用 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;
}
動畫格線
版面配置動畫是由 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>
元素選項也能計算在內。
<select>
元素選擇反映在 counter()
中
在資料正規化部分中,我們已根據輸入建立事件監聽器。這個函式結束時,系統會知道所選篩選器的數量,以及這些篩選器的結果數量。這些值可傳送至狀態角色元素,如下所示。
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 個結果」的公告完成。
如今,我們將為所有使用者提供優異的輔助技術體驗,無論他們如何與輔助技術互動。
結論
既然你知道我如何做到,你會怎麼做呢? 🙂
讓我們多方嘗試,瞭解在網路上建構應用程式的所有方式。建立示範、張貼推文 連結,以便我們將其新增至下方的社群重混專區!
社群重混作品
尚無任何資料可提供!