button 比 div 按钮
把用于执行操作的 div 按钮替换为原生 button,跳转地址仍用 a,并显式设置 type 为 button 或 submit、重置默认样式,以获得键盘操作、读屏语义和表单联动等原生能力。
学完这篇,你能一眼认出代码里那些"长得像按钮的 div",把它安全地换成原生 <button>,并且知道哪几种情况下反而不能换。
第一步:先确认你要改的代码长什么样
这一步要做的是找到页面里所有"用 div 假装按钮"的地方,改完之后你手上会有一份明确的清单,而不是凭感觉零散地改。
典型的"div 按钮"长这样:
<div class="btn" onclick="submitForm()">提交</div>
<div class="btn primary" @click="handleSave">保存</div>
判断标准很简单:**它被点击后执行一个动作(保存、删除、展开、切换),而不是跳转到一个新地址。**
按动作类型分两类:
- 跳转地址(打开详情页、下载文件、跳到外链)→ 应该用
<a href="...">,不是<button>,也不是<div>。 - 执行操作(提交表单、打开弹窗、切换开关、删除一项)→ 应该用
<button>。
注意:
<div onclick>在真实项目里常常成片出现,因为它们都被同一个.btn样式类控制。改之前先把.btn样式在哪些文件里被引用搜一遍(VS Code 里Ctrl+Shift+F搜.btn),否则改完 HTML 样式全散。
第二步:搞清楚 <button> 白送你多少东西
这一步不做任何代码修改,只是让你明白替换的价值,避免改到一半觉得"没区别"。
一个原生 <button> 自带这些能力,而 <div> 全都要你自己写 JS 补:
- 可聚焦:按
Tab键能走到它,<div>默认不在 Tab 序列里。 - 键盘触发:
Enter和空格键都能触发点击,<div>一个都不支持。 - 无障碍语义:屏幕阅读器(NVDA、VoiceOver)会念出"按钮,提交",
<div>只会念出里面的文字,用户不知道这是可以按的。 - 表单联动:在
<form>里,<button>天然是提交按钮,不用写 JS。 - 状态表达:
disabled属性直接生效,读屏软件会念出"不可用"。 - 移动端行为:真实的按钮元素在触屏上有正确的点击反馈和焦点处理。
第三条是很多人低估的。你可以现在打开 Chrome,按 F12 打开 DevTools,切到 Elements 面板,选中一个 <div class="btn">,看右侧标签栏里点开 Accessibility 选项卡,Computed Properties 里的 Role 会显示 generic;换成 <button> 再看,Role 是 button,Name 是按钮里的文字。
第三步:动手把一个 div 按钮改成 button
这一步把清单上的第一个 div 按钮改掉,改完你会得到一个键盘可用、读屏可读的真按钮。
改法分三部分:改标签、补 type、重置默认样式。
1)改标签,把事件绑定保留
<!-- 改前 -->
<div class="btn" onclick="submitForm()">提交</div>
<!-- 改后 -->
<button type="button" class="btn" onclick="submitForm()">提交</button>
框架项目同理:
<!-- 改前 -->
<div class="btn primary" @click="handleSave">保存</div>
<!-- 改后 -->
<button type="button" class="btn primary" @click="handleSave">保存</button>
2)type 必须显式写
HTML 规范里 <button> 的默认 type 是 submit。也就是说,如果你在一个 <form> 里写了一个没写 type 的按钮,点它整个表单就提交了,还带着刷新页面的副作用。
规则记死:
- 只是执行一个 JS 动作 →
type="button" - 真的要提交表单 →
type="submit"(并且不要给它绑 JS,让表单的submit事件统一处理)
3)重置浏览器默认样式
<button> 带着一套浏览器默认外观,直接加上你的 .btn 会发现颜色、圆角、字体都不对。在项目的基础样式里加一段全局重置:
button {
font: inherit; /* 关键:否则按钮内字体不继承父级,字体族和字号都会变 */
color: inherit;
background: none;
border: 0;
padding: 0;
cursor: pointer;
appearance: none; /* 去掉 iOS Safari 的原生圆角和渐变 */
}
注意:
font: inherit这一行最容易被漏掉。漏了之后,即使你的.btn写了font-size: 14px,按钮里如果放的是纯文字也可能显示成系统默认字号;而按钮里放<span>时字体又正常,于是出现"同一个按钮里文字大小不一致"的诡异现象。同一条规则也顺手解决line-height不继承的问题。
第四步:处理"按钮里有复杂内容"的情况
这一步解决你改造时会碰到的最常见的阻力:原本的 div 里塞了图标、多层 span、甚至用 flex 排版,换成 button 后布局变了。
推荐写法是外面用 <button>,里面包一层 <span> 承担 flex 布局:
<button type="button" class="btn">
<span class="btn__inner">
<svg class="icon" aria-hidden="true">...</svg>
保存
</span>
</button>
.btn__inner {
display: inline-flex;
align-items: center;
gap: 6px;
}
之所以绕一层,是因为 <button> 在浏览器里是特殊的替换元素,历史上 Firefox 和 Safari 直接把 display: flex 写在 <button> 上有过布局异常,Chrome 在按钮内部作为 flex 容器时也出现过子元素点击不冒泡到按钮的问题。包一层 <span> 就完全绕开了这些历史包袱,多写一行换来长期稳定。
另外两条硬规则:
<button>里不能再放<button>、<a>、<input>这类本身可聚焦的元素,嵌套会导致焦点混乱,屏幕阅读器读出来的结构也是错的。- 图标要标
aria-hidden="true";如果按钮里只有图标没有文字,按钮上加aria-label="关闭",否则读屏只会念出"按钮",用户不知道是什么按钮。
第五步:自测,确认改对了
这一步在浏览器里做四项检查,全部通过就说明这个按钮改合格了。
- Tab 键:按
Tab依次走一遍页面,每个按钮都能聚焦,并且焦点框肉眼可见。如果看不到,检查你的 CSS 里是不是写了outline: none。要自定义焦点样式,用:focus-visible,不要全局干掉 outline。
- 键盘触发:聚焦到按钮上,按
Enter能触发,按空格也能触发。(如果你偷懒用了<div role="button" tabindex="0">,就必须自己写keydown同时监听Enter和Space,而且Space还要e.preventDefault()防止页面滚动——这些<button>全都免费。)
- 读屏确认:DevTools 的 Elements 面板 → Accessibility 选项卡,看 Role 是不是
button、Name 是不是你想要的名字。
- 自动扫描:DevTools 顶部标签栏找 Lighthouse 面板,只勾选 Accessibility,点 Analyze page load,跑完后看 "Buttons do not have an accessible name" 这类条目。或者在 Chrome 应用商店装 axe DevTools 扩展,点扩展图标 → Scan ALL of my page,它会直接列出缺名字、对比度不足的按钮。
第六步:认清不能换成 button 的三种情况
这一步帮你避免"一刀切"改错。
- 导航跳转:会改变 URL 的都用
<a href>。<a>支持中键新标签页打开、右键"复制链接"、被搜索引擎抓取,<button>都不支持。 - 整块卡片可点击且内部还有链接:比如商品卡片整体可点,右上角还有个"收藏"。这种不要用 button 包
a,正确做法是卡片里放一个绝对定位铺满的<a>("拉伸链接"模式),收藏按钮用z-index排在它上面。 - 纯装饰或语义是别的角色:比如折叠面板的标题本身是标题,交互应该由标题内的一个
<button aria-expanded="true" aria-controls="panel-1">承担,而不是把<h2>整个变成按钮。
还有一个小坑:真的需要"看起来能点但点不了"的状态时,优先用 disabled 属性;但如果你的场景要求"禁用状态仍能被 Tab 聚焦并提示原因"(比如表单校验未通过),改用 aria-disabled="true" 加 JS 拦截,因为 disabled 的按钮会被直接踢出 Tab 序列,键盘用户会完全找不到它。
小结
- 执行动作的用
<button>,跳转地址的用<a href>,<div onclick>只在无法改造的历史代码里留。 <button>白送键盘聚焦、Enter/空格触发、读屏语义、表单提交、disabled状态,<div role="button">全部要手写而且容易漏。- 永远显式写
type,表单里写type="submit",其余一律type="button",否则会莫名其妙触发表单提交。 - 基础样式重置里必须有
font: inherit,否则按钮内文字字体不继承。 - 按钮内需要 flex 布局时,外面用
<button>,里面包一层<span>做 flex 容器,绕开浏览器历史兼容问题。 - 按钮里不要嵌套
<button>/<a>/<input>;纯图标按钮要加aria-label。 - 改完必须用 Tab 键、
Enter/空格、DevTools 的 Accessibility 面板和 Lighthouse 四项检查验收。
原文链接:https://www.gj0.com/thread-1796.html
转载请注明出处并保留本声明;内容仅代表作者观点,与本站立场无关。若本文涉嫌侵权,请联系本站处理。