构建多选组件

简要介绍如何构建自适应、自适应、易于访问的多选组件,用于对用户体验进行排序和过滤。

在这篇博文中,我想分享一下如何构建多选组件。试用 演示

<ph type="x-smartling-placeholder">
</ph>
演示

如果你更喜欢视频,可以参阅此博文的 YouTube 版本:

概览

用户经常会看到一些项,有时是大量项, 因此建议您提供一种方法来缩减列表, 选择过载。这个 这篇博文探讨了通过过滤界面来减少选择。它通过 呈现用户可以选择或取消选择的商品属性,减少结果数量 从而减少选择过载。

互动次数

目的是为所有用户及其用户启用快速遍历过滤选项 不同的输入类型。这将通过一个可适应的、响应迅速的 一对组件包含桌面设备和键盘复选框的传统边栏 和屏幕阅读器,以及 <select multiple>

显示桌面版浅色和深色带边栏的比较屏幕截图
与具有多选元素的移动 iOS 和 Android 设备的复选框对比。

这种针对触摸(而不是桌面)使用内置多选的决定可以节省工作量和创造工作,但我认为,与在一个组件中构建完整的响应体验相比,它可以减少代码负担,从而提供合适的体验。

轻触

触摸组件可节省空间,并有助于提高 。它可以将整个复选框边栏折叠为一个文本框,从而节省空间。 <select> 内置叠加层触摸体验。它通过显示 系统提供的大尺寸触摸叠加层体验。

答
Android、iPhone 和 版 Chrome 中多选元素的屏幕截图预览
iPad。iPad 和 iPhone 的多选功能处于打开状态,并且每人都有一个
专为屏幕尺寸优化的独特体验

键盘和游戏手柄

下面演示了如何通过键盘使用 <select multiple>

这种内置的多选选项无法设置样式,并且仅在紧凑型中提供 不适合呈现大量选项。看看您为什么不能真正 在这个小方框中看到了各种选项?你可以更改其大小 还是不如多选复选框的适用性

Markup

这两个组件将包含在同一个 <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 现在可以识别复选框界面和状态角色的常规计算 元素为空,正在等待值。由于 CSS 会在 内存、 counter() 函数允许从伪造对象访问 元素内容:

aside #applied-filters::before {
  content: counter(filters) " filters ";
}

状态角色元素的 HTML 现在会读出“2 个过滤器”到屏幕 读取器这是一个良好的开端,但我们可以做得更好,例如分享 结果。我们将使用 JavaScript 执行这项工作 计数器的功能之外。

MacOS 屏幕阅读器的屏幕截图,其中显示已启用的滤镜数量。

积累经验

计数器算法在 CSS 中效果很好 “nesting-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> 的位置会导致很难创建等距的集合。 儿童。不是 gap,而是相邻的同级元素 selectormargin-block-start

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 对数据进行标准化处理。

DevTools 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 可以监视与复选框的交互并在过滤 网格中,像 <select> 元素一样添加 textContent

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 屏幕阅读器的屏幕截图,其中显示读出结果。

现在,我们卓越的辅助技术体验 无论他们如何与之互动

总结

现在您已经知道我是怎么做到的了,您该怎么做 ‽ 🙂?

让我们一起采用多样化的方法,学习所有在 Web 上构建应用的方法。 创建演示,在 Twitter 微博上添加链接,然后我会添加 到下面的社区混剪部分!

社区混剪作品

此处尚无任何可显示的内容!