使用React构建一个可访问的通用组件:级联选择器

一、为什么要构建可访问的组件

在开发web应用程序时,访问性是非常重要的。Web内容应该对所有人都是可用的,无论他们的能力或技术水平如何。这意味着需要确保我们构建的应用程序能访问到各种各样的用户,包括有残障的用户。

如果你需要构建一个级联选择器组件,那么它必须是可访问的,以确保每个人都可以使用它。用户可以使用键盘导航,屏幕阅读器等多种方式来与应用程序交互。

二、如何构建可访问的级联选择器

构建可访问的级联选择器时需要考虑以下问题:

1.表格结构

在使用React构建可访问的级联选择器时,首先需要考虑如何为组件呈现表格结构。


  class CascadingSelect extends React.Component {
    render() {
      const { options } = this.props;

      return (
        <table className="cascading-select">
          <thead>
            <tr>
              <th>Option 1</th>
              <th>Option 2</th>
              <th>Option 3</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <select aria-label="Option 1" className="option-1">
                  {options.map(option => (
                    <option key={option.value} value={option.value}>{option.label}</option>
                  ))}
                </select>
              </td>
              <td>
                <select aria-label="Option 2" className="option-2">
                  <option>Please select an option</option>
                </select>
              </td>
              <td>
                <select aria-label="Option 3" className="option-3">
                  <option>Please select an option</option>
                </select>
              </td>
            </tr>
          </tbody>
        </table>
      );
    }
  }

2.键盘导航

为了使级联选择器对于键盘用户友好,需要进行正确的键盘导航。

使用方向键向上和向下浏览可选项,并且通过键盘选择选项。在此过程中,UI需要提供正确的反馈,例如高亮所选项并将其保存为活动状态。

当选择过程完成后,级联选择器需要使新选择的选项处于活动状态,并将焦点设置为级联选择器下一个可操作单元格中的适当选项。


  class CascadingSelect extends React.Component {
    handleKeyDown = (e, column, row) => {
      const { options, onChange } = this.props;
      const activeOption = options[column][row];
      const activeOptionIndex = options[column].indexOf(activeOption);

      switch (e.keyCode) {
        case 13: // Return
        case 32: // Space
          e.preventDefault();
          onChange(column, row, activeOption.value);
          break;
        case 38: // Up arrow
          e.preventDefault();
          if (activeOptionIndex > 0) {
            onChange(column, row, options[column][activeOptionIndex - 1].value);
          }
          break;
        case 40: // Down arrow
          e.preventDefault();
          if (activeOptionIndex < options[column].length - 1) {
            onChange(column, row, options[column][activeOptionIndex + 1].value);
          }
          break;
        case 37: // Left arrow
          e.preventDefault();
          if (column > 0) {
            const prevRow = options[column - 1].findIndex(option => option.value === options[column - 1][row].parent);
            this[`select${column - 1}-${prevRow}`].focus();
          }
          break;
        case 39: // Right arrow
          e.preventDefault();
          if (column < options.length - 1 && options[column + 1].some(option => option.parent === activeOption.value)) {
            const nextRow = options[column + 1].findIndex(option => option.parent === activeOption.value);
            this[`select${column + 1}-${nextRow}`].focus();
          }
          break;
        default:
          break;
      }
    };

    render() {
      const { options, values } = this.props;

      return (
        <table className="cascading-select">
          ...
          <tbody>
            {options[0].map((option, row) => (
              <tr key={option.value}>
                <td>
                  <select
                    aria-label="Option 1"
                    className="option-1"
                  >
                    {options[0].map(option => (
                      <option key={option.value} value={option.value} selected={values[0] === option.value}>
                        {option.label}
                      </option>
                    ))}
                  </select>
                </td>
                {options.slice(1).map((column, columnIdx) => (
                  <td key={columnIdx}>
                    <select
                      aria-label={`Option ${columnIdx + 2}`}
                      className={`option-${columnIdx + 2}`}
                      disabled={values[columnIdx] === ''}
                      onChange={(e) => this.props.onChange(columnIdx + 1, row, e.target.value)}
                      onKeyDown={(e) => this.handleKeyDown(e, columnIdx + 1, row)}
                      value={values[columnIdx + 1]}
                      ref={(el) => { this[`select${columnIdx + 1}-${row}`] = el; }}
                    >
                      <option value="">Please select an option</option>
                      {column.filter(o => o.parent === values[columnIdx]).map(option => (
                        <option key={option.value} value={option.value} selected={values[columnIdx + 1] === option.value}>
                          {option.label}
                        </option>
                      ))}
                    </select>
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      );
    }
  }

3.屏幕阅读器

屏幕阅读器将表格朗读给残障用户,以使他们能够使用级联选择器。

如果级联选择器中的选项值不像视觉用户那样易于识别,可以添加自定义aria-label来为屏幕阅读器提供更多信息并更好地帮助残障用户使用级联选择器。


  class CascadingSelect extends React.Component {
    render() {
      const { options } = this.props;

      return (
        <table className="cascading-select" aria-label="Cascading select">
          ...
          <tbody>
            {options[0].map((option, row) => (
              <tr key={option.value}>
                <td>
                  <label htmlFor={`option-1-${row}`} className="visually-hidden">Option 1</label>
                  <select
                    id={`option-1-${row}`}
                    aria-label="Option 1"
                    className="option-1"
                  >
                    ...
                  </select>
                </td>
                {options.slice(1).map((column, columnIdx) => (
                  <td key={columnIdx}>
                    <label htmlFor={`option-${columnIdx + 2}-${row}`} className="visually-hidden">Option {columnIdx + 2}</label>
                    <select
                      id={`option-${columnIdx + 2}-${row}`}
                      aria-label={`Option ${columnIdx + 2}`}
                      className={`option-${columnIdx + 2}`}
                      disabled={values[columnIdx] === ''}
                      onChange={(e) => this.props.onChange(columnIdx + 1, row, e.target.value)}
                      onKeyDown={(e) => this.handleKeyDown(e, columnIdx + 1, row)}
                      value={values[columnIdx + 1]}
                      ref={(el) => { this[`select${columnIdx + 1}-${row}`] = el; }}
                    >
                      ...
                    </select>
                  </td>
                ))}
              </tr>
            ))}
          </tbody>
        </table>
      );
    }
  }

三、总结

在React中构建可访问的级联选择器很重要,因为它确保您的web应用程序可以被尽可能多的用户使用。通过使用简单的表格结构,正确定义键盘导航,添加相关的aria-label和label元素,可以容易地创建可访问的级联选择器。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/280616.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-21 13:04
下一篇 2024-12-21 13:04

相关推荐

  • 如何修改ant组件的动效为中心

    当我们使用Ant Design时,其默认的组件动效可能不一定符合我们的需求,这时我们需要修改Ant Design组件动效,使其更加符合我们的UI设计。本文将从多个方面详细阐述如何修…

    编程 2025-04-29
  • Ant Design组件的动效

    Ant Design是一个基于React技术栈的UI组件库,其中动效是该组件库中的一个重要特性之一。动效的使用可以让用户更清晰、更直观地了解到UI交互的状态变化,从而提高用户的满意…

    编程 2025-04-29
  • 用mdjs打造高效可复用的Web组件

    本文介绍了一个全能的编程开发工程师如何使用mdjs来打造高效可复用的Web组件。我们将会从多个方面对mdjs做详细的阐述,让您轻松学习并掌握mdjs的使用。 一、mdjs简介 md…

    编程 2025-04-27
  • Spring MVC主要组件

    Spring MVC是一个基于Java语言的Web框架,是Spring Framework的一部分。它提供了用于构建Web应用程序的基本架构,通过与其他Spring框架组件集成,使…

    编程 2025-04-27
  • Mescroll.js——移动端下拉刷新和上拉加载更多组件

    一、概述 Mescroll.js是一款移动端的下拉刷新和上拉加载更多组件,因其简单易用和功能强大而深受开发者的喜爱。Mescroll.js可以应用于各种移动端网站和APP,能够支持…

    编程 2025-04-25
  • Vue强制重新渲染组件详解

    一、Vue强制重新渲染组件是什么? Vue中的强制重新渲染组件指的是,当我们需要重新渲染组件,但是组件上的数据又没有改变时,我们可以使用强制重新渲染的方式来触发组件重新渲染。这种方…

    编程 2025-04-25
  • Vue封装公共组件的最佳实践

    一、封装公共组件的意义 随着前端技术的不断发展,Web应用程序变得越来越复杂。为了更好地管理和维护代码,我们通常需要编写可重用的组件,而这些组件往往是我们所写的多个项目都需要用到的…

    编程 2025-04-25
  • 深度解析Ant Design中Table组件的使用

    一、Antd表格兼容 Antd是一个基于React的UI框架,Table组件是其重要的组成部分之一。该组件可在各种浏览器和设备上进行良好的兼容。同时,它还提供了多个版本的Antd框…

    编程 2025-04-25
  • UMY-UI组件库详解——一款优秀的React组件库

    随着前端组件化的风潮,越来越多的组件库被开发出来。其中,UMY-UI便是一款优秀的React组件库。 一、基本介绍 UMY-UI是基于React框架开发的一套UI组件库,提供了丰富…

    编程 2025-04-24
  • React 子组件调用父组件方法

    一、基本介绍 React 是一个非常流行的 JavaScript 库,用于构建用户界面。React 的主要思想是组件化,允许将 UI 拆分为独立的、可复用的部分。React 组件有…

    编程 2025-04-23

发表回复

登录后才能评论