你以为完美的按钮,其实是个大bug
为什么你的按钮总是出bug?
每个前端开发者都经历过这个。
产品经理甩过来一张设计稿,上面是一个"特别"的按钮。要渐变色,要 hover 动效,按下去还要有反馈。设计师追求完美,这很正常。
于是你二话不说拉了个 <div>,配上 class="btn" 和一堆 CSS。预览效果,完美。设计师点头,通过。
然后用户开始用了——
有人用 Tab 键导航,发现按钮根本进不去焦点顺序。有人按空格键提交表单,没反应。有人开着屏幕阅读器,压根不知道这是个可以点的按钮。
你开始疯狂打补丁:加 click 事件、加 focus 样式、加键盘监听、加上 role="button"、tabindex="0"……
最后你写了两百行 JavaScript,就为了模拟一个浏览器天然就能给你的东西。
这就是"技术债"的利息。你当初选错了元素,每一行 workaround 都是你欠下的债。
原生元素的好处,没人告诉你
<button> 这个标签看起来平平无奇,但它背后是一整套交互协议。
用真正的 button:
- 键盘交互:回车、空格键按预期工作
- 焦点管理:自然进入 Tab 顺序,还能被 JavaScript 主动 focus
- 禁用状态:自动阻止所有交互,自动从焦点链移除,屏幕阅读器会播报"已禁用"
- 表单联动:能提交表单、参与表单验证、响应 form.reset()
- 无障碍语义:读屏软件直接识别为按钮,不需要额外 ARIA
- 平台一致:用户知道按钮怎么用,因为所有网站按钮都长一个样
用一个 div + CSS 模拟的按钮呢?
只有你的 CSS。剩下全是活儿。
而且说实话,不管你多仔细,你的实现永远比不上浏览器内置的方案。浏览器背后有专业团队在优化各种边界情况、各种无障碍场景。你的自定义按钮,只有你自己。
真正的问题不是"能不能样式化"
很多开发者的误解在于:以为用语义化 HTML 就意味着接受浏览器丑丑的默认样式。
完全不是一回事。
问题从来不是"能不能自定义外观"。当然可以——加 appearance: none 就能抹掉所有默认样式。渐变、阴影、动画、特殊字体,随便加。浏览器根本不管你长什么样。
问题应该是:"原生功能里,哪些需要保留?"
<button> 可以变成任何你想要的视觉形态,但它的交互能力还在。
而用 div 模拟的话,每一样都要你自己写。没有捷径。
所以,想清楚你要把工程时间花在哪里。花在真正让产品与众不同的地方,而不是重新发明轮子。
你的用户在真实的世界里
设计稿是静态的,但用户不是。
- 有人在用 Tab 切焦点,手没离开键盘
- 有人在用语音控制
- 有人把页面放大到 300% 才能看清
- 有人用 Safari,有人用 Firefox,有人用 IE(虽然快没了)
- 有人开着读屏软件
原生 HTML 元素天然就适配这些场景。语义化元素天生可访问,天生支持键盘导航,天生跨平台兼容。
你选择对抗这套基础设施,不只是给自己找麻烦——你做出来的东西,会在某些用户那里以各种奇怪的方式出问题。
下次想用 <div role="button"> 的时候,问自己两个问题:
- 我是不是真的需要一个自定义实现?
- 还是我只是不喜欢
<button>这个标签?
答案往往是后者。
用最朴素的方式解决问题,这不是偷懒。浏览器替你把事情做对了,这是功能,不是限制。
语义化不是为了遵守规则。是把对的东西用在对的场景上,然后把你的精力花在真正重要的地方。