为什么用 button 标签比 div 做按钮更好?有哪些坑

juming
juming 初级会员超兽战士
发布于 2026-10-08 09:36 ·2 浏览 ·0 回复

**结论:做按钮就该用原生 <button>,因为它自带键盘可操作(Tab 聚焦、Enter/Space 触发)、焦点管理和无障碍语义,而 <div> 做按钮必须手写 role="button" + tabindex="0" + 双键处理 + 自己模拟禁用态,任何一环漏掉就是 bug;但 <button> 本身有 4 个高频坑——忘记写 type="button"、默认样式与字体不继承、disabled 导致不可聚焦、内部排版受匿名盒子影响。**

button 比 div 白送你哪些能力?

原生 <button> 免费提供 6 项能力,<div> 全都要自己实现:

  1. 进入 Tab 顺序,tabindex 默认就是 0,不需要手动加;
  2. Enter 在 keydown 触发、Space 在 keyup 触发——这是原生按钮的真实行为,也是手写最容易做错的细节;
  3. 角色语义为 role=button,屏幕阅读器直接读成「按钮」,而不只是读出文字;
  4. 可访问名称自动取自按钮内的文本(Accessible Name);
  5. 禁用态 disabled 原生生效,浏览器同时阻止点击和表单提交;
  6. 表单集成:放在 <form> 内可触发表单提交,name/value 会随表单一起提交。

用 <div> 复刻,最小代码量是这样:

<div role="button" tabindex="0" aria-disabled="false" id="btn">提交</div>
<script>
btn.addEventListener('keydown', e => {
  if (e.key === 'Enter') { doAction(); }
  if (e.key === ' ') e.preventDefault();   // 防止空格滚动页面
});
btn.addEventListener('keyup', e => {
  if (e.key === ' ') doAction();           // 空格在抬起时触发
});
</script>

这段代码只覆盖了原生按钮 20% 的行为,还要自己补焦点样式、禁用逻辑、触屏点击延迟等问题。

坑一:不写 type="button" 会误提交表单

结论:<button> 的默认 type 是 submit,不是 button。 在 <form> 里写一个用来做「取消」「展开」的按钮却没写 type,点一下整个表单就提交了,页面刷新、数据丢失。同理,用户在表单输入框里按 Enter 时,浏览器会默认激活表单里第一个 type="submit" 的按钮。

养成习惯:只要不是提交按钮,一律显式写 type="button"。

坑二:默认样式和字体不继承

**结论:<button> 不会继承父元素的 font-family 和 font-size,浏览器会套用系统 UI 字体。** 常见后果是同页面按钮文字比正文大一号、字体不一致;另外 Chrome、Safari 下单行按钮默认 align-items: center,Firefox 的额外内边距来自 ::-moz-focus-inner(老版本需要 border: 0; padding: 0 清掉)。

统一重置模板可以直接抄:

button {
  appearance: none;
  -webkit-appearance: none;   /* iOS Safari 去掉圆角/渐变默认外观 */
  font: inherit;              /* 关键:修复字体不继承 */
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

注意:浏览器默认给 <button> 的 cursor 是 default,不是 pointer,鼠标手型要自己加。

坑三:disabled 按钮点不着也读不到

**结论:disabled 的按钮会被移出 Tab 顺序,屏幕阅读器无法聚焦它,用户看不到「为什么不能点」的提示。** 如果你需要给禁用按钮加 tooltip 或错误说明,用 aria-disabled="true" 代替 disabled,并在点击处理函数里提前 return,这样按钮仍可聚焦、仍会被读出来,同时保持视觉置灰。

WCAG 2.2 的 2.5.8 条款要求点击目标至少 24×24 CSS 像素(AAA 级为 44×44),移动端按钮做小了这个是硬伤。

坑四:内部不能放交互元素,排版别直接交给 button

结论:<button> 的内容模型是 phrasing content,且禁止嵌套交互内容,所以 <button> 里塞 <a>、<input> 是无效 HTML;<div> 虽然浏览器能渲染,但不合规范。

另一个细节:部分浏览器对 <button> 直接设置 display: flex / display: grid 表现不一致,因为按钮内部有一个匿名盒子参与布局。稳妥做法是内层再包一个 <span>,把 flex/grid 排版交给 span:

<button type="button">
  <span class="btn-inner"><svg/><span>下载</span></span>
</button>

什么时候该用 a 标签而不是 button?

结论:导航用途用 <a>,执行动作才用 <button>。 点击后跳转到新 URL、可被右键「在新标签页打开」、可被复制链接——这些是链接的语义,用 <button> 或 <div> 都会丢失。反过来,提交表单、打开弹窗、切换状态这类不改变 URL 的操作,用 <a href="#"> 是错的,它会让用户在浏览器历史里留下垃圾记录。

一句话收束:默认选 <button> 并显式写 type="button",样式用上面的 reset 模板统一,禁用态优先 aria-disabled,布局交给内层 span;只有在「跳转」场景才换成 <a>。

版权声明:本文来自 GJ站长论坛《为什么用 button 标签比 div 做按钮更好?有哪些坑》
原文链接:https://www.gj0.com/thread-875.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。

全部回复 0

还没有回复,来抢沙发~