什么时候按钮不是按钮?
已发表: 2022-03-10假设您有用户单击界面的一部分并发生了一些事情。 对我来说,这听起来像是一个按钮,但我们暂时称它为“可点击的东西”。 我知道,你相信它也是一个按钮:它是圆形的,并以漂亮的番茄色突出,要求与之交互。 但让我们想一想。 从长远来看,这会节省时间,我保证。
如果这个可点击的东西中的文本是“阅读更多”,并且点击它会将用户引导到另一个页面上的文章怎么办? 唔。 如果有一个带蓝色下划线的单词“关闭”来关闭弹出对话框怎么办? 它是一个链接,只是因为它是蓝色的并带有下划线吗? 当然不是。
哇! 似乎无法仅通过查看它来判断它是链接还是按钮。 太疯狂了! 在选择正确的元素之前,我们需要了解这个东西的作用。 但是,如果我们还不知道它在做什么或者只是感到困惑怎么办? 好吧,我们有一个方便的流程图:
- 这是一个按钮。
- 如果没有,那么它是一个链接。
- 而已。
那么,一切都是按钮吗? 不,但您总是可以从一个按钮开始,用于几乎任何可以以类似方式单击或交互的元素。 如果它缺少某些东西,例如导航到另一个页面,请改用链接。 不,指针不是使其成为<a href>
的理由。 我们有cursor: pointer
。
好吧,这是一个<button>
按钮——我们同意这一点。 让我们把它放在我们的模板中,并根据设计对其进行样式设置:一些填充、舍入、番茄填充、白色文本,甚至一些焦点样式。 哦,你真好。
<button type="button" class="button"> Something </button> <style> .button { display: inline-block; padding: 10px 20px; border-radius: 20px; background-color: tomato; color: white; } .button:focus { outline: none; box-shadow: 0 0 0 5px #006AE3; } </style>
没过多久。 您想快速构建它并吃点午餐,因为您饿了。 好的,让我们看看它的外观并开始吧。
我的天啊! 浏览器有问题。 为什么这个按钮这么难看? 文本很小,即使我们明确地将body
设置为16px
,甚至font-family
也是错误的。 带有傻乎乎的伪阴影的圆形边框是如此复古,以至于它甚至还不是一种趋势。
啊,这是浏览器的默认样式。 您需要小心地撤消它,甚至添加 Normalize.css 或 Reset.css……或者您可以只使用<div>
并忘记它。 他们付钱给你的不是快速解决问题吗? 你饿了,这根本没有帮助。 但你是专业人士:振作起来思考。
<button>
和<div>
有什么区别呢? 内置的<button>
是一个交互元素,这意味着它可以被交互。 这很深。 你可以点击它,你可以使用键盘专注于它,它还向屏幕阅读器传达了一个可访问的button
角色,让用户可以理解它是一个按钮。
感人的! 您不仅了解 HTML 的<button>
元素,而且还对 ARIA 和屏幕阅读器支持有所了解。 您甚至可能尝试过 VoiceOver 或 NVDA 来测试您的界面的可访问性。
所以,你决定做一个把戏。 您不会弄乱浏览器的样式,并且会使元素看起来像一个适合可能需要它的用户的交互式按钮。 这很聪明!
<div class="button" tabindex="0" role="button"> Something </div>
现在它不仅看起来正确,而且由于tabindex="0"
属性,它可以通过键盘获得焦点,并且屏幕阅读器会将它视为正确的按钮,因为您已经明智地添加了role="button"
到它。 然后 Git 提交 && 推送! 这件事还有一些额外的任务,但我们已经完成了样式。 什么可能出错? 午餐时间。 太好了,我们走吧!
一小时后…
那是一顿美味的午餐! 让我们回到我们点击的东西。 在继续之前,我们需要完成一些任务。 让我们看看……我们需要在单击按钮后调用doSomething
函数,并且应该有一种方法可以禁用按钮,使其不可单击。 听起来很容易。 让我们为这个按钮添加一个事件监听器:
<script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); }); function doSomething() { console.log('Something!'); } </script>
完毕。 用户现在可以在桌面上用鼠标单击它,然后用手指在触摸屏上点击它。 点击事件会可靠地触发,你会看到很多东西! 在您的控制台中。 下一个任务是什么?
坚持,稍等! 我们需要确保它对键盘用户同样有效。 因为我们在按钮上有这个tabindex="0"
,所以它可以被聚焦,一旦它被聚焦,用户应该能够按空格键或“Enter”键来触发我们附加的任何内容。
因此,我们需要附加另一个事件侦听器来捕获所有按键,并且我们将仅针对某些按键触发我们的函数。 感谢上帝,触摸设备足够智能,可以将所有点击转换为点击; 否则,我们也必须附加一堆触摸事件。
<script> const buttons = document.querySelectorAll('.button'); [...buttons].forEach(button => { button.addEventListener('click', doSomething); button.addEventListener('keyup', (event) => { if (event.key == 'Enter' || event.key == ' ') { doSomething(); } }); }); function doSomething() { console.log('Something!'); } </script>
呸! 现在我们的 clicky 东西可以从键盘完全访问了。 我很为你骄傲! JavaScript 真的很神奇——没有它我们该怎么办?
好吧,最后一个任务是什么:“按钮应该有一个禁用状态,它的外观和行为会变得麻木。” 麻木的? 我想这意味着一些灰色的东西,对交互没有反应。 好的,让我们使用 BEM 命名在样式表中添加一个状态。
<div class="button button--disabled" tabindex="0" role="button"> Something </div> <style> .button--disabled { background-color: #9B9B9B; } </style>
这对我来说看起来很麻木。 每当需要禁用按钮时,我们将添加button--disabled
修饰符使其变为灰色。 但它还不够麻木:它仍然可以通过指针和键盘来聚焦和触发。
该死,这变得越来越棘手。
不仅如此,按钮不应该按 Tab 顺序访问,这意味着tabindex
属性不应该在那里。 我们需要检查按钮是否处于禁用状态,然后停止触发我们的功能。 此外,该修饰符可以动态应用。 虽然 CSS 动态匹配元素与选择器并应用样式不是问题,但我们可能需要某种突变观察器来触发此按钮的其他更改。
我知道,对吧? 我们认为这将是一个触发功能并处于禁用状态的简单小按钮。 我们试图通过可访问性和所有这些东西来使它正确,现在我们深陷这个兔子洞。
我们去吃点外卖吧。 当我们完成并正确测试时,我们不会回家吃晚饭。 该死的W3C! 为什么他们不尝试让我们的生活更轻松? 好像他们在乎我们一样!
事实上,他们确实……
在跳入这个烂摊子之前,让我们退后几步。 为什么我们不尝试使用<button>
元素来做这些事情呢? 它有一些有用的技巧,而不仅仅是浏览器的丑陋风格。 哦,别忘了type="button"
— 你不希望弹出窗口的“关闭”按钮意外提交表单,因为type="submit"
是默认值。
显然,当<button>
获得焦点并按下空格键或“Enter”键时,它将触发click
事件,就像移动设备在获得点击、拍拍、舔或任何其他他们能够接收的内容时所做的那样今天。 我们的代码中少了一个事件监听器! 好的。
// A click is enough! button.addEventListener('click', doSomething);
至于禁用状态, disabled
属性可用于<button>
元素以及所有表单元素,包括<fieldset>
。 不开玩笑。 您是否知道只需将单个属性应用于父<fieldset>
就可以禁用一大堆组合在一起的输入?
<fieldset disabled> <legend>A bunch of numb inputs</legend> <p> <label> <input type="radio" name="option"> Of course it's a link </label> </p> <p> <label> <input type="radio" name="option"> Obviously, it's a button </label> </p> <p> <label> <input type="radio" name="option"> I just wanna go home </label> </p> <button type="button">Button</button> </fieldset>
现在你知道了! 此属性不仅禁用表单元素上的所有事件,而且还将它们从 Tab 键顺序中删除。 问题解决了!
<button disabled type="button" class="button"> Something </button>
但是等等,还有更多! 它还触发了 CSS 中的:disabled
伪类,这意味着我们可以去掉 BEM 修饰符来声明样式,而使用内置的动态修饰符。
.button:disabled { background-color: #9B9B9B; }
至于浏览器的丑陋样式,我们不必使用所有的 Normalize.css 来修复单个按钮。 将其用作智慧的来源:下面的三行额外的行将解决与<div>
的大部分恼人差异。 如果您需要更多,您可以从中复制相关部分。
.button { font-size: 100%; font-family: inherit; border: none; }
完毕。 毕竟 HTML 并没有那么糟糕!
但是,如果它时不时地让您感到惊讶,请务必检查 HTML 规范以获取答案。 多年来,它变得更加友好,并且充满了良好的用法和可访问性示例。 当然,好的 ol' HTML5 Doctor 仍然是一个可靠的地方,可以找出<section>
和<article>
元素之间的区别,并检查文档大纲是否存在(不是真的)。 您很有可能最终也会阅读 Mozilla 的 HTML 文档,而且您也不会后悔。
这个任务现在完成了! 下一步是什么? 带有搜索字段的下拉轮播日历? 天啊! 祝你好运。 但请记住: <button>
是你的朋友!
关于 SmashingMag 的进一步阅读:
- 如何设计更好的按钮
- 构建包容性切换按钮
- 幽灵按钮设计:这真的还是一件事(为什么)?
- 设计模式:当打破规则是可以的