什么时候按钮不是按钮?

已发表: 2022-03-10
快速总结↬并不是所有的圆形和突出的东西都被认为是一个按钮。 在这篇文章中,Vadim 解释了如何为您的用户创建一个合适的交互式按钮——一个不应该与其他任何东西混淆的按钮。

假设您有用户单击界面的一部分并发生了一些事情。 对我来说,这听起来像是一个按钮,但我们暂时称它为“可点击的东西”。 我知道,你相信它也是一个按钮:它是圆形的,并以漂亮的番茄色突出,要求与之交互。 但让我们想一想。 从长远来看,这会节省时间,我保证。

类似番茄按钮的东西,上面有“Something”文字。
为您的“可点击的东西”设计(大预览)

如果这个可点击的东西中的文本是“阅读更多”,并且点击它会将用户引导到另一个页面上的文章怎么办? 唔。 如果有一个带蓝色下划线的单词“关闭”来关闭弹出对话框怎么办? 它是一个链接,只是因为它是蓝色的并带有下划线吗? 当然不是。

类似按钮的链接和类似链接的按钮
链接或按钮困境(大预览)
跳跃后更多! 继续往下看↓

哇! 似乎无法仅通过查看它来判断它是链接还是按钮。 太疯狂了! 在选择正确的元素之前,我们需要了解这个东西的作用。 但是,如果我们还不知道它在做什么或者只是感到困惑怎么办? 好吧,我们有一个方便的流程图:

流程图:这是一个按钮。如果不是,那么它是一个链接。而已。
选择正确元素的科学流程图(大预览)
  1. 这是一个按钮。
  2. 如果没有,那么它是一个链接。
  3. 而已。

那么,一切都是按钮吗? 不,但您总是可以从一个按钮开始,用于几乎任何可以以类似方式单击或交互的元素。 如果它缺少某些东西,例如导航到另一个页面,请改用链接。 不,指针不是使其成为<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 的进一步阅读

  • 如何设计更好的按钮
  • 构建包容性切换按钮
  • 幽灵按钮设计:这真的还是一件事(为什么)?
  • 设计模式:当打破规则是可以的