自定义 UI 的隐藏复杂性:为什么你应该使用语义化 HTML 按钮
自定义 UI 的隐藏复杂性:为什么你应该使用语义化 HTML 按钮
从头开始重新创建一个原生的 HTML 按钮是一项西西弗斯式的艰巨任务,因为一个简单的 <button> 元素封装了数十年的无障碍标准、浏览器行为和操作系统集成。为了使用自定义元素来复制原生按钮,开发者必须手动实现从键盘焦点和 ARIA 角色到复杂的表单验证和提交逻辑的一切。
原生按钮的要求
要真正替换原生按钮,自定义组件必须满足一套严格的 UX "法律" 和无障碍要求。根据 MDN、W3C ARIA 规范和 WCAG 2.2,一个功能完备的按钮必须:
- 角色与标签: 拥有
button角色和清晰的无障碍标签 (WCAG SC 4.1.2, 1.3.1)。 - 焦点管理: 能够通过键盘获取焦点,并且在获得焦点时是可见的 (WCAG SC 2.4.3, 2.4.7)。
- 输入模式: 通过鼠标点击、触摸、手写笔和其他指向设备激活。
- 键盘交互: 在获得焦点时,专门通过 Space 和 Enter 键激活 (WCAG SC 2.1.1)。
- 表单集成: 支持类型 (
submit,reset,button),参与表单验证,并处理诸如formaction,formmethod, 和formtarget之类的属性 (WCAG SC 3.3.1)。 - 状态管理: 支持诸如
disabled之类的状态 (WCAG SC 4.1.2)。 - 现代 API 支持: 与 Popover API 或 Invoker Commands API 等较新的浏览器功能集成。
实现差距:从标记到 500 行代码
从头开始构建一个按钮揭示了简单的 HTML 标签与为所有用户提供功能所需的底层逻辑之间存在的巨大差距。
1. 无障碍与焦点
自定义标签(例如 <sagan-button>)默认被视为 <span>。为了使其成为一个按钮,开发者必须手动添加 role="button" 和 tabindex="0" 以确保它出现在无障碍树和页面的标签顺序中。对于图标按钮,还需要额外的工作,使用 aria-hidden="true" 隐藏装饰性的表情符号或图标,并提供描述性的 aria-label 以避免屏幕阅读器朗读表情符号的字面名称。
2. 事件处理与模式
原生按钮会自动处理各种输入方法。自定义实现必须显式监听 mouseup, touchend, 和 onpointerup 以覆盖鼠标、触摸和手写笔交互。此外,键盘无障碍性要求为 keydown (Enter 键) 和 keyup (Space 键) 提供单独的逻辑,以符合原生浏览器的预期。
3. 表单关联与验证
原生按钮最复杂的方面之一是它与 HTML 表单的关系。为了复制这一点,自定义元素必须设置 static formAssociated = true 并为包括 type, form, formaction, formmethod, formenctype, formnovalidate, formtarget, name, 和 value 在内的属性实现大量的 getter 和 setter。
它还必须与 Validation API 集成,实现诸如 checkValidity() 和 reportValidity() 之类的方法,并通过 ElementInternals 管理 validity 状态。
4. 状态与事件顺序
为了防止自定义按钮行为异常,开发者必须封装 addEventListener 方法。这确保了内部逻辑(如设置 "active" 状态或检查按钮是否已禁用)相对于用户定义的事件监听器以正确的顺序运行。如果没有这一点,如果在内部处理程序注册不正确,在点击事件上调用 preventDefault() 可能会无法停止按钮的默认行为。
专家见解综述
开发者之间的讨论强调了,追求自定义组件的驱动力往往优先考虑美学而非可用性和无障碍性。
- 可用性 vs. 美学: 有人认为,行业正在用功能完备、高对比度的原生组件换取 "flat nonsense"(扁平化废话),这些组件通常使用起来更慢且不够直观。
- "无障碍性陷阱": 有报道称,一些公司因为无障碍性不合规而被起诉,结果却被咨询顾问 "拯救",因为顾问只是移除了复杂的 ARIA 标签并恢复了网站的标准语义化 HTML。
- AI 的角色: 虽然有人建议 AI 现在可以在几分钟内生成自定义组件所需的样板代码,但其他人警告说,AI 通常是在不可访问的代码上训练的,这可能会大规模传播不良的无障碍模式。
- 缺失的原生元素: 一个常见的反驳观点是,自定义组件有时是必要的,因为原生 HTML 缺乏某些复杂的元素,例如带有服务器端过滤功能的 combobox。
结论:语义化 HTML 的重要性
从头开始重新创建一个按钮需要近 500 行 JavaScript 来实现仅仅是单个 <button> 标签所提供功能的一小部分。维护负担、因果无障碍性失败导致的法律风险,以及用户体验的降级,使得自定义构建的基础 UI 元素成为一个低效的选择。除非针对特定模式不存在所需的原生元素,否则语义化 HTML 仍然是 Web 开发的金标准。