使用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/zh-tw/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

發表回復

登錄後才能評論