You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React/TypeScript单选按钮Tabindex无法聚焦后续元素求助

问题解决:React/TypeScript单选按钮Tab切换失效

问题根源分析

你的代码里有几个导致Tab切换异常的关键点:

  • DOM结构不合理:每个单选按钮被单独的<ul>包裹,非语义化结构干扰了浏览器的焦点导航顺序。
  • 冗余的tabIndex设置:单选按钮(type="radio")原生就是可聚焦元素,默认tabIndex=0,手动重复设置可能引发焦点逻辑冲突。
  • onFocus强制修改选中状态:聚焦时自动设置checked=true,破坏了浏览器原生的焦点交互逻辑,干扰Tab键的正常切换流程。
  • react-focus-trap使用不当:未正确包裹目标元素或配置初始焦点,导致焦点无法被限制在指定区域内。

分步解决方案

1. 修复DOM结构

将所有单选按钮放在同一个<ul>下的<li>中,保证语义化结构,避免干扰焦点顺序:

return (
  <ul>
    {answers.map((a, index) => (
      <li key={index}>
        {a.answerType === "option"
          ? radioBtn(a, index)
          : a.answerType === "optionText"
          ? radioBtnText(a, index)
          : null}
      </li>
    ))}
  </ul>
);

2. 移除冗余配置与不合理逻辑

去掉手动设置的tabIndex={0},删除onFocus中强制选中的代码,恢复原生交互:

const radioBtn = (a: SurveyAnswer, index: number) => {
  return (
    <label>
      <input
        autoFocus={index === 0}
        className="form-radio"
        type="radio"
        name="myRadio"
        value={a.answer}
        onClick={(e) => handleChange((e.target as HTMLInputElement).value)}
        onChange={() => setDisabledInput("")}
      />
      <span className="mx-2">{a.answer}</span>
    </label>
  );
};

const radioBtnText = (a: SurveyAnswer, index: number) => {
  return (
    <label>
      <input
        autoFocus={index === 0}
        type="radio"
        className="form-radio"
        name="myRadio"
        value={a.answer}
        onClick={() => setDisabledInput(a.answer)}
        onChange={() => {}}
      />
      <span className="mx-2">
        {a.answer}
        <input
          className="form-input rounded"
          type="text"
          disabled={disabledInput !== a.answer}
          onChange={(e) =>
            handleChange(a.answer + (e.target as HTMLInputElement).value)
          }
        />
      </span>
    </label>
  );
};

3. 正确使用react-focus-trap

确保焦点陷阱包裹所有需要聚焦的元素,并配置初始焦点目标,让Tab键在区域内循环:

import FocusTrap from 'react-focus-trap';

// 在组件返回部分包裹
return (
  <FocusTrap focusTrapOptions={{ initialFocus: '[name="myRadio"]:first-of-type' }}>
    <ul>
      {answers.map((a, index) => (
        <li key={index}>
          {/* ... 单选按钮内容 ... */}
        </li>
      ))}
    </ul>
  </FocusTrap>
);

补充:原生单选组的交互逻辑

注意:同name的单选按钮组,浏览器默认行为是Tab键聚焦到整个组,用方向键切换选项,这是符合无障碍标准的设计。如果非要用Tab键逐个切换每个单选按钮,需要给每个按钮设置不同的name,并手动处理互斥选中逻辑(不推荐,会破坏无障碍支持)。

内容的提问来源于stack exchange,提问作者avenmia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:13:21