按钮用div还是button,为什么总说原生button才是王道?

1,990字
8–13 分钟
in

网页上那些能点的东西,看着都一样,背地里却可能藏着两种完全不同的命。一种是用<div>硬装出来的按钮,一种就是原生的<button>。好多时候图省事,随手就丢个<div role="button">上去,心想反正能点就行,用户又看不出来。可这玩意儿真没那么简单,这里面差的可不是一星半点,而是从骨子里就决定了这玩意到底好不好用,尤其是对那些不用鼠标、靠键盘或者读屏软件上网的人来说,体验简直是天差地别。

目录

为啥用div冒充button会被喷?

以前总觉得说用语义标签就是“为了规范”,听起来跟喊口号似的。真正上手比划比划,才发现这俩玩意在浏览器眼里压根就不是一个物种。打开开发者工具,用那个检查元素的功能一看,原生<button>自带的那些属性,<div>基本全得靠手动硬编码去补,而且补得还不一定对。

在开发者工具里扒一扒它们的底裤

拿俩东西放一块对比,啥都清楚了。先整一个正宗的<button>自定义按钮</button>,再搞一个<div class="btn" role="button">自定义按钮</div>,然后用浏览器那个审查元素的家伙,点开“无障碍属性”或者“属性”那一栏,差别一下就显出来了。

特性对比原生按钮模拟按钮
角色标识按钮通用
标签内容按钮文字
默认聚焦支持不支持
键盘触发空格回车不支持

原生那个直接就把自己“按钮”的身份亮出来了,里面的文字自动就成了给读屏软件听的标签,而且键盘一按Tab键,焦点框“啪”一下就套上去了,敲空格或者回车,它立马就有反应。再看那个<div>,身份就是个“通用”玩意儿,没标签,Tab键按到死也切不到它身上,敲键盘更是一点动静没有。

给div强行续命的那些骚操作

有些头铁的朋友可能会说,这有啥难的,给它补上不就完了?行,那就看看给这个<div>把功能补齐得花多少功夫。光加个role="button"还远远不够,那只是告诉读屏软件“我像个按钮”,但实际行为一点没变。得手动给它加上tabindex="0",这样Tab键才能切到它。可就算切上去了,敲空格和回车还是没反应,还得写JavaScript去监听键盘事件。而且空格键和回车键在浏览器里干的活还不一样,得分别处理,一个管滚动页面不触发的场景,一个管触发的场景,稍微不注意就写漏了。更别提还得自己实现一个禁用状态,原生按钮加个disabled属性就完事,<div>这边又得折腾一堆属性监听和样式覆盖。

功能点原生实现模拟实现
禁用状态加属性即可写逻辑加样式
焦点管理浏览器自带手动加索引
键盘交互自动支持脚本监听
语义暴露自动识别手动加角色

这弯道超车的代价也太大了,本来一行标签能搞定的事,非要写成几十行代码,还容易出岔子。给<div>加个role="button",就好比给自行车贴了个“法拉利”的标,蹬起来该累还是累,该没顶棚还是没顶棚。

想把button样式随心改,有什么化繁为简的办法?

很多人死抱着<div>不放,无非是嫌原生<button>自带了太多默认样式,什么边框、背景、内边距,每个浏览器长得还不一样,重置起来感觉麻烦。其实这就有点因噎废食了,重置样式根本就是一行CSS的事,比重新造轮子省心多了。

一行CSS让button变白纸

写样式的时候,直接给所有按钮来个“格式化”操作,把那些自带的花里胡哨统统抹掉。就这一句:

button {
  all: unset;
}

这行代码跟卸妆似的,把<button>身上所有浏览器强加的样式全都清干净,连默认的盒子模型都给你回归到最原始状态。清完以后,它就跟<div>一样白纸一张,想怎么画就怎么画。要是觉得all: unset太猛了,也可以只挑几个关键属性重置:

button {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

这么一套组合拳下来,<button>那点“臭脾气”就全被捋顺了。剩下的就是用同一个CSS类名,比如.btn,给原生按钮和模拟按钮套上同样的样式,从外观上根本看不出区别,但背后的功能却一个天上一个地下。

样式克隆,功能不掉队

写样式的时候,完全可以设计一个.btn类,把圆角、背景、阴影、悬停效果都定义好。然后:

<button class="btn">点我提交</button>
<div class="btn" role="button">点我提交</div>

俩东西摆在一起,视觉上一模一样。可真正跑起来,<div>那个除了长得像,键盘用户根本够不着,屏幕阅读器也不确定这玩意儿到底是啥。所以与其在这上面纠结那点重置样式的时间,不如直接养成习惯,凡是能点的交互控件,无脑用<button>就对了。样式的事,本来就应该交给CSS去管,HTML负责把结构的底子打好。那些以为用<div>能弯道超车的,最后往往都在可访问性测试上翻车翻得焦头烂额。