建立複選元件

概略說明如何建構回應式、適應性且無障礙功能,並提供多選元件,以便排序及篩選使用者體驗。

在這篇文章中,我想分享如何建立複選元件的想法。立即試用示範

示範

如果你偏好使用影片,也可以觀看這篇 YouTube 文章:

總覽

使用者經常會看到項目,有時是「許多」項目,這時建議您提供減少清單的方式,避免選項超載。這篇網誌文章將探討篩選 UI 來減少選擇的方法。其運作方式為顯示使用者可以選取或取消選取的項目屬性,進而減少結果,進而減少選項超載。

互動

目標是為所有使用者及其各種輸入類型啟用迅速遍歷選項。您可以透過可彈性調整、回應及回應的元件組合來達成。適用於電腦、鍵盤和螢幕閱讀器的傳統核取方塊,以及觸控使用者適用的 <select multiple>

顯示電腦淺色和深色的比較螢幕截圖,其中有核取方塊 (適用於 iOS 和 Android 行動裝置,各有一個複選元素)。

因此,我們決定使用內建的多選功能 (而非電腦) 儲存工作和建立工作,但我認為,比起在單一元件中建構完整的回應式體驗,我相信能夠以更少的程式碼債務打造合適的體驗。

觸控

觸控元件可節省空間,並協助行動裝置使用者的互動準確度。這項功能會將整個核取方塊側欄收合至 <select> 內建重疊觸控體驗,藉此節省空間。它可以顯示系統提供的大型觸控重疊體驗,協助輸入準確度。

Android、iPhone 和 iPad 版 Chrome 中的多選元素螢幕截圖預覽畫面。iPad 和 iPhone 皆開啟複選功能,每人都能享有針對螢幕大小最佳化的獨特體驗。

鍵盤和遊戲手把

以下示範如何在鍵盤中使用 <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 執行這項作業,因為這是計數器不支援的功能。

MacOS 螢幕閱讀器的螢幕截圖,顯示目前套用的濾鏡數量。

加入巢狀結構

計數器演算法非常適合 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;
}
這張螢幕截圖顯示在多行包裝情境中,勾號與第一行文字對齊的方式。
觀看 Codepen 的其他內容

動畫格線

版面配置動畫是由 Isotope 完成。效能強大、功能強大的外掛程式,適用於互動排序及篩選。

JavaScript

除了協助自動化調度管理簡潔的動畫互動格狀外,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 筆結果」。

MacOS 螢幕閱讀器朗讀結果的螢幕截圖。

現在,我們擁有優異的輔助技術,無論使用者如何進行互動,都能獲得優異的輔助技術體驗。

結論

現在既然你已經知道我怎麼做,你會怎麼做‽ 🙂?

讓我們帶您更多元的方法,並瞭解運用網路打造網站的所有方式。 請建立示範並透過 Twitter 推文連結,我就能將這項工具新增至下方的「社群重混」部分!

社群重混作品

這裡還沒有內容!