链接和按钮指南

Avatar of Chris Coyier
Chris Coyier

我们关于 HTML、CSS 和 JavaScript 中链接、按钮和按钮式输入的完整指南。

由 DigitalOcean 提供

DigitalOcean 提供您在任何阶段增长所需的云计算服务。 立即获取免费的 200 美元信用额度!

在 HTML 中,关于链接和按钮有很多要了解的内容。包括标记实现和相关属性,样式最佳实践,要避免的事项,以及链接的更微妙的“表亲”:按钮和按钮式输入。

让我们来看看链接和按钮的整个世界,以及它们在 HTML、CSS、JavaScript、设计和可访问性层面上需要考虑的所有因素。在此过程中,有很多陷阱和不良实践需要避免。通过涵盖这些内容,我们将对这两种元素的良好 UX 实现有一个完整的理解。

何时使用每个元素的快速 **指南**

  • 您要为用户提供一种方法让他们转到另一个页面或同一页面的另一个位置吗?使用 **链接** (<a href="/somewhere">链接</a>)
  • 您要创建一个 JavaScript 驱动的可点击操作吗?使用 **按钮** (<button type="button">按钮</button>)
  • 您要提交表单吗?使用 **提交输入** (<input type="submit" value="提交">)

链接是 Web 中最基本、最基础、最核心的构建模块之一。点击一个链接,您将跳转到另一个页面,或跳转到同一页面的另一个位置。

目录
<a href="https://css-tricks.org.cn">CSS-Tricks</a>

这是一个指向“完全限定”或“绝对”URL 的链接。

您也可以“相对”链接。

<!-- Useful in navigation, but be careful in content that may travel elsewhere (e.g. RSS) -->
<a href="/pages/about.html">About</a>

这可能很有用,例如,在开发中,域名可能与生产站点不同,但您仍然希望能够点击链接。相对 URL 最适合导航等操作,但在内容中(例如博文)使用时要小心,因为这些内容可能在其他地方被阅读,例如在应用程序或 RSS 订阅源中。

链接也可以通过以 # 开头来成为“哈希链接”或“跳转链接”。

<a href="#section-2">Section Two</a>
<!-- will jump to... -->
<section id="section-2"></section>

点击该链接将“跳转”(滚动)到 DOM 中第一个 ID 与之匹配的元素,例如上面的 section 元素。

💥 **小技巧:**在开发中使用哈希链接(例如 #0)可能会有用,这样您就可以点击链接而不会像点击 # 链接那样被发送回页面顶部。但要小心,不指向任何位置的链接永远不应该出现在生产环境中。

💥 **小技巧:**跳转链接有时可以从 平滑滚动 中获益,以帮助用户理解页面正在从一个地方移动到另一个地方。

在网站上看到一个“返回顶部”链接是很常见的 UI/UX 现象,尤其是在重要导航控件位于顶部但有大量内容需要滚动(或以其他方式导航)时。要创建一个跳转链接,请链接到页面顶部元素的 ID,该元素是将焦点发送回去的最佳位置。

<a href="#top-of-page">Back to Top</a>

跳转链接有时也用于链接到其他没有 href 属性的锚点 (<a>) 元素。这些被称为“占位符”链接。

<a id="section-2"></a>
<h3>Section 2</h3>

这些链接有一些 可访问性考虑因素,但总体而言是可以接受的。

没有 href 属性的链接是 唯一实用方法 来禁用链接。为什么要禁用链接?也许它是一个只有在登录或注册后才能激活的链接。

a:not([href]) {
  /* style a "disabled" link */
}

当链接没有 href 时,它没有角色、没有可聚焦性,也没有键盘事件。 这是故意的。您可以把它想象成 <span>

您可以为此使用 target 属性,但这 强烈不建议

<a href="https://css-tricks.org.cn" target="_blank" rel="noopener noreferrer">
  CSS-Tricks
</a>

使其起作用的部分是 target="_blank",但请注意那里有额外的 rel 属性和值,这些值 使其更安全,而且 更快

让链接在新选项卡中打开是一个重大的 UX 讨论。我们在这方面有一篇关于 何时使用它的完整文章。总结如下

不要使用它

  • 因为您或您的客户个人偏好。
  • 因为您正在尝试提高您在网站上的停留时间指标。
  • 因为您正在区分内部链接和外部链接或内容类型。
  • 因为这是您逃避处理无限滚动复杂性的方法。

使用它

  • 因为用户正在当前页面上执行某些操作,例如主动播放媒体或有未保存的工作。
  • 您有一些模糊的技术原因迫使您这样做(即使那样,您可能仍然是例外而不是规则)。

链接上的 download 属性 将指示浏览器下载链接文件,而不是在当前页面/选项卡中打开它。这是一个不错的 UX 功能。

<a href="/files/file.pdf" download>Download PDF</a>

rel 属性

此属性用于链接与目标的关系。

rel 属性也常用于 <link> 元素(它不用于创建超链接,而是用于诸如包含 CSS 和预加载之类的事情)。我们这里不包括 <link> 元素的 rel 值,只包括锚点链接。

以下是一些基本示例

<a href="/page/3" rel="next">Next</a>
<a href="/page/1" rel="prev">Previous</a>

<a href="http://creativecommons.org/licenses/by/2.0/" rel="license">cc by 2.0</a>

<a href="/topics/" rel="directory">All Topics</a>
  • rel="alternate":文档的备用版本。
  • rel="author":文档的作者。
  • rel="help":有关文档的帮助资源。
  • rel="license":许可证和法律信息。
  • rel="manifest":Web 应用清单文档。
  • rel="next":系列中的下一篇文章。
  • rel="prev":系列中的上一篇文章。
  • rel="search":用于在当前文档中执行搜索的文档。

还有一些 rel 属性专门用于 告知搜索引擎

  • rel="sponsored":将广告或付费展示(通常称为付费链接)标记为赞助。
  • rel="ugc":对于不太可靠的用户生成内容,例如评论和论坛帖子。
  • rel="nofollow":告诉搜索引擎忽略此链接,不要将此网站与链接到的网站关联起来。

还有一些以安全性为中心的 rel 属性

  • rel="noopener":防止新选项卡使用 JavaScript window.opener 功能,该功能可能会访问包含链接的页面(您的网站)以执行恶意操作,例如窃取信息或共享受感染的代码。与 target="_blank" 一起使用通常 是一个好主意
  • rel="noreferrer":防止其他网站或跟踪服务(例如 Google Analytics)将您的页面识别为点击链接的来源。

如果需要,您可以使用多个空格分隔的值(例如 rel="noopener noreferrer"

最后,一些rel属性来自微格式标准独立网络,例如

  • rel="directory":表示超链接的目标是包含当前页面条目的目录列表。
  • rel="tag":表示该超链接的目标是当前页面的作者指定“标签”(或关键字/主题)。
  • rel="payment":表示该超链接的目标提供了一种显示或支持当前页面的方式。
  • rel="help":表示链接到的资源是当前文档的帮助文件或常见问题解答。
  • rel="me":表示其目标代表与当前页面相同的个人或实体。

ARIA 角色

链接的默认角色是link,因此你不需要执行

<a role="link" href="/">Link</a>

你只需要在伪造链接时这样做,这将是一件奇怪/罕见的事情,你必须使用一些 JavaScript 来完成它,以便它真正遵循链接。

<span class="link" tabindex="0" role="link" data-href="/">
  Fake accessible link created using a span
</span>

只需查看上面的内容,你就可以看到伪造链接需要多少额外的工作,并且这还没有考虑它会破坏右键单击,不允许在新标签页中打开,不适用于 Windows 高对比度模式和其他阅读器模式以及辅助技术。很糟糕!

一个有用的 ARIA 角色来指示当前页面,例如

<a href="/" aria-current="page">Home</a>
<a href="/contact">Contact</a>
<a href="/about">About/a></a>

你应该使用title属性吗?

可能不会。将其保留用于为iframe提供简短的描述性标题。

<a title="I don't need to be here" href="/">
  List of Concerts
</a>

title提供了一个悬停触发的 UI 弹出窗口,显示你编写的文本。你无法对其进行样式化,并且它实际上并没有那么易于访问

悬停触发是这里的关键短语。它在任何仅限触控的设备上都不可用。如果链接需要更多上下文信息,请在链接周围的实际内容中提供该信息,或者使用链接本身的描述性文本(而不是像“点击这里”这样的东西)。

如果链接中只包含一个图标,例如

<a href="/">😃</a>

<a href="/">
  <svg> ... </svg>
</a>

这不足以提供有关链接的上下文信息,特别是对于辅助功能的原因,但可能对任何人都有用。带有文本的链接几乎总是更清晰。如果你绝对无法使用文本,你可以使用类似以下模式的内容

<a href="/">
  <!-- Hide the icon from assistive technology -->
  <svg aria-hidden="true" focusable="false"> ... </svg>
  <!-- Acts as a label that is hidden from view -->
  <span class="visually-hidden">Useful link text</span>
</a>

visually-hidden是一个用于使用 CSS 视觉隐藏标签文本的类

.visually-hidden {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

aria-label不同,视觉隐藏文本可以翻译,并且在专用浏览模式中会保持更好的效果。

如果你将图像包裹在链接中,图像可以是链接。无需使用 alt 文本来说明图像是一个链接,因为辅助技术已经会这样做。

<a href="/buy/puppies/now">
  <img src="puppy.jpg" alt="A happy puppy.">
</a>

你可以链接整个内容区域,例如

<a href="/article/">
  <div class="card">
    <h2>Card</h2>
    <img src="..." alt="...">
    <p>Content</p>
  </div>
</a>

但当然,也存在 UX 影响。例如,选择文本可能更难,并且整个元素需要相当复杂的样式才能创建清晰的焦点和悬停状态。也存在辅助功能影响,例如在宣布为链接之前,会先读取整个卡片的内容。

以下是一个包含两种方法的示例。第一个将整个卡片包裹在链接中。这是有效的,但请记住影响。第二个在标题中包含一个链接,并且链接上有一个覆盖整个卡片的伪元素。这也有一些影响(例如,选择文本有点尴尬),但对于辅助技术来说可能更符合预期。

第二个示例还打开了包含多个链接的可能性。你不能嵌套链接,所以如果你需要嵌套链接,事情会变得有点棘手。尽管可以通过将各个链接设置在覆盖卡片的链接之上,并使用 z-index 来实现

以下是链接的默认外观

链接的默认用户代理样式。

你很可能要更改链接的样式,并且也很可能使用 CSS 来完成。我可以通过以下方式在 CSS 中将所有链接设置为红色

a {
  color: red;
}

有时,选择和样式化页面上的所有链接有点太过分了,因为导航中的链接可能与文本中的链接完全不同。你始终可以范围选择器来针对特定区域内的链接,例如

/* Navigation links */
nav a { }

/* Links in an article */
article a { }

/* Links contained in an element with a "text" class */
.text a { }

或者直接选择链接进行样式化。

.link {
  /* For styling <a class="link" href="/"> */
}

a[aria-current="page"] {
  /* You'll need to apply this attribute yourself, but it's a great pattern to use for active navigation. */
}

链接是可聚焦元素。换句话说,它们可以使用键盘上的Tab键进行选择。链接可能是你非常有意识地设计不同状态(包括:focus状态)的最常见元素。

  • :hover:用于在鼠标指针悬停在链接上时进行样式化。
  • :visited:用于在链接已被跟随时进行样式化,尽可能地按照浏览器能够记住的方式进行。由于安全性问题,它具有有限的样式化能力。
  • :link:用于在链接被访问时进行样式化。
  • :active:用于在链接被按下时(例如鼠标按钮被按下或在触控屏上点击元素)进行样式化。
  • :focus:非常重要!链接应该始终具有焦点样式。如果你选择删除大多数浏览器应用的默认蓝色边框,也使用此选择器来重新应用一个视觉上明显的焦点样式

这些可以像任何伪类一样进行链接,因此如果你觉得对你的设计/UX 有用,你可以这样做。

/* Style focus and hover states in a single ruleset */
a:focus:hover { }

也许链接和按钮之间的某些混淆是因为类似这样

来自Katherine Kato的非常酷的“按钮”样式。

这当然看起来像一个按钮!每个人都会称它为按钮。即使是设计系统也可能称它为按钮,并且可能有一个类似.button { }的类。但是!你可以点击的、上面写着“了解更多”的东西绝对是一个链接,而不是一个按钮。这完全没问题,它只是又一次提醒你使用语义和功能上正确的元素。

颜色对比度

由于我们经常使用不同的颜色对链接进行样式化,因此使用具有足够颜色对比度的颜色以实现辅助功能非常重要。存在各种视觉障碍(请参阅工具WhoCanUse 以模拟具有不同障碍的配色方案),并且高对比度几乎可以帮助所有障碍。

也许你为链接设置了一个蓝色

蓝色链接是 #2196F3。

虽然这对你来说可能看起来不错,但最好使用测试工具来确保颜色符合研究指南的要求,并具有足够的对比度。在这里,我将使用 Chrome DevTools,它会告诉我这种颜色不符合要求,因为它与背后的背景色对比度不够。

Chrome DevTools 告诉我们,此链接颜色没有足够的对比度。

颜色对比度是链接的一个重要考量因素,不仅仅因为链接通常使用独特的颜色,需要检查,而且因为它们有各种不同的状态(悬停、焦点、活动、访问),这些状态可能也有不同的颜色。再加上文本可以被选中,你有很多地方需要考虑对比度。 这里有一篇文章介绍了所有这些。

我们可以使用 CSS 中的属性选择器来识别链接指向的资源类型,前提是 href 值中包含有用的信息。

/* Style all links that include .pdf at the end */
a[href$=".pdf"]::after {
  content: " (PDF)";
}

/* Style links that point to Google */
a[href*="google.com"] {
  color: purple;
}

CSS 有一种“at-规则”,用于声明仅对打印媒体生效的样式(例如,打印网页)。你可以将它们包含在任何 CSS 中,如下所示

@media print {
  /* For links in content, visually display the link */ 
  article a::after { 
    content: " (" attr(href) ")";
  }
}

重置样式

如果你需要移除链接(或任何其他元素)的所有样式,CSS 提供了一种使用 all 属性来移除所有样式的方法。

.special-area a {
  all: unset;
  all: revert;
  
  /* Start from scratch */
  color: purple;
}

你也可以使用关键字移除单个样式。(同样,这并不只针对链接,而是通用的方法)

a {
  /* Grab color from nearest parent that sets it */
  color: inherit;

  /* Wipe out style (turn black) */
  color: initial;

  /* Change back to User Agent style (blue) */
  color: revert;
}

假设你想要阻止链接的点击执行其默认行为:跳转到该链接或在页面中跳转。在 JavaScript 中,你可以使用 preventDefault 来阻止跳转。

const jumpLinks = document.querySelectorAll("a[href^='#']");

jumpLinks.forEach(link => {
 link.addEventListener('click', event => {
    event.preventDefault();
    // Do something else instead, like handle the navigation behavior yourself
  });
});

这种方法是“单页面应用”(SPA)工作原理的核心。它们拦截点击,以防止浏览器接管并处理导航。

SPA 会查看你尝试跳转到的位置(在你的网站内),加载所需的数据,替换页面上需要替换的部分,并更新 URL。这相当于复制浏览器免费提供的功能,需要做很多工作,但你可以实现页面之间的动画等效果。

另一个与链接相关的 JavaScript 问题是,当点击指向另一个页面的链接时,会离开当前页面并加载另一个页面。对于包含用户正在填写但未完成的表单的页面,这可能是一个问题。如果他们点击链接并离开页面,他们将丢失他们的工作!你唯一阻止用户离开的机会是使用 beforeunload 事件。

window.addEventListener("beforeunload", function(event) {
  // Remind user to save their work or whatever.
});

默认行为被移除的链接不会宣布新的目标。这意味着使用辅助技术的使用者可能不知道他们最终到了哪里。你将不得不做一些事情,比如更新页面的标题并将焦点重新移到文档的顶部。

JavaScript 框架

在 JavaScript 框架(如 React)中,你可能有时会看到链接是从类似 <Link /> 组件而不是原生 <a> 元素创建的。自定义组件可能会创建一个原生 <a> 元素,但会添加额外的功能,例如启用 JavaScript 路由器工作,并根据需要添加 aria-current="page" 等属性,这很好!

最终,链接就是链接。JavaScript 框架可能会提供或鼓励一定程度的抽象,但你始终可以自由地使用常规链接。

我们在上面的部分中介绍了一些可访问性(它们都是相关的!),但这里还有一些需要考虑的事情。

  • 链接文本本身不需要像“链接”或“前往”这样的文字。使文本更有意义(使用“文档”而不是“点击此处”)。
  • 链接默认情况下已经具有 ARIA 角色(role="link"),因此无需显式设置它。
  • 尽量不要使用 URL 本身作为文本(<a href="google.com">google.com</a>)。
  • 链接通常是蓝色的,通常带下划线,这通常很好
  • 内容中的所有图像都应该有 alt 文本,但当图像被包裹在没有其他文本的链接中时,这一点更加重要。

独特的可访问名称

一些辅助技术可以创建页面上的交互元素列表。想象一下一组四个文章卡片,它们都包含一个“阅读更多”,交互元素的列表将类似于

  • 阅读更多
  • 阅读更多
  • 阅读更多
  • 阅读更多

没什么用。你可以使用我们介绍过的 .visually-hidden 类,使链接更像

<a href="/article">
  Read More
  <span class="visually-hidden">
    of the article "Dancing with Rabbits".
  <span>
</a>

现在每个链接都是唯一的,清晰明了。如果设计允许,请不要使用隐藏的类,以消除对所有人的歧义。

按钮

按钮用于触发操作。何时使用 <button> 元素?一个好的规则是,当没有“有意义的 href”时使用按钮。换句话说,如果点击它在没有 JavaScript 的情况下不做任何事情,它应该是一个 <button>

<button> 元素如果在 <form> 中,默认情况下会提交表单。但除此之外,按钮元素没有任何默认行为,你将使用 JavaScript 来连接交互行为。

目录

HTML 实现

<button>Buy Now</button>

<form> 中的按钮具有默认行为:提交表单!它们也可以重置表单,就像它们的输入对应项一样。type 属性很重要

<form action="/" method="POST">
  <input type="text" name="name" id="name">
  <button>Submit</button>

  <!-- If you want to be more explicit... -->
  <button type="submit">Submit</button>

  <!-- ...or clear the form inputs back to their initial values -->
  <button type="reset">Reset</button>

  <!-- This prevents a `submit` action from firing which may be useful sometimes inside a form -->
  <button type="button">Non-submitting button</button>
</form>

说到表单,按钮有一些巧妙的技巧,它们可以覆盖 <form> 本身的属性。

<form action="/" method="get">

  <!-- override the action -->
  <button formaction="/elsewhere/" type="submit">Submit to elsewhere</button>

  <!-- override encytype -->
  <button formenctype="multipart/form-data" type="submit"></button>

  <!-- override method -->
  <button formmethod="post" type="submit"></button>

  <!-- do not validate fields -->
  <button formnovalidate type="submit"></button>

  <!-- override target e.g. open in new tab -->
  <button formtarget="_blank" type="submit"></button>

</form>

自动获取焦点

由于按钮是可聚焦元素,因此我们可以使用 autofocus 属性在页面加载时自动将焦点设置到它们上面

<div class="modal">

  <h2>Save document?</h2>

  <button>Cancel</button>
  <button autofocus>OK</button>
</div>

也许你会在模态对话框中这样做,其中一个操作是默认操作,它有助于 UX(例如,你可以按 Enter 关闭模态)。在用户操作之后自动获取焦点可能是这里唯一的最佳实践,在没有用户许可的情况下移动用户的焦点,就像 autofocus 属性能够做到的那样,可能会给屏幕阅读器和屏幕放大镜使用者带来问题。

请注意,由于安全原因,如果元素位于 <iframe sandbox> 中,autofocus 可能不起作用。

禁用按钮

要阻止按钮具有交互性,可以使用 disabled 属性

<button disabled>Pay Now</button>
<p class="error-message">Correct the form above to submit payment.</p>

请注意,我们在禁用的按钮旁边包含了描述性文本。发现一个禁用的按钮却不知道为什么被禁用,这可能会让人感到非常沮丧。更好的方法可能是让用户提交表单,然后在验证反馈消息中解释为什么提交失败。

无论如何,你可以用这种方式来为禁用的按钮设置样式

/* Might be good styles for ANY disabled element! */
button[disabled] {
  opacity: 0.5;
  pointer-events: none;
} 

我们将在本指南的后面部分介绍其他状态和样式。

按钮可以包含子元素

提交按钮和提交输入(<input type="submit">)在功能上是相同的,但不同之处在于输入无法包含子元素,而按钮可以。

<button>
   <svg aria-hidden="true" focusable="false">
     <path d="..." />
   </svg>
   <span class="callout">Big</span>
   Sale!
</button>

<button type="button">
  <span role="img" aria-label="Fox">
    🦊
  </span>
  Button
</button>

请注意上面 SVG 元素上的 focusable="false" 属性。在这种情况下,由于图标是装饰性的,这将有助于辅助技术只宣布按钮的标签。

样式和 CSS 注意事项

按钮通常被设计成看起来很像按钮。它们应该看起来可以按下。如果您正在寻找花哨按钮样式的灵感,建议您看看 CodePen 上的按钮主题

1, 2, 3, 4, 5, 6

跨浏览器/平台按钮样式

按钮的默认外观因浏览器和平台而异。

仅在 macOS 上:Chrome、Safari 和 Firefox(它们看起来相同)
在上面相同的按钮中添加 border: 0;,我们会得到完全不同的样式。

虽然保留表单元素的默认值以便它们与浏览器/平台的样式相匹配,并获得一些免费的 可供性 确实有一些 UX 方面的道理,但设计师通常不喜欢默认样式,尤其是跨浏览器不同的样式。

重置默认按钮样式

从按钮中删除所有样式比您想象的要容易。您可能会认为,作为表单控件,appearance: none; 会有所帮助,但不要指望它。实际上 all: revert; 是更好的选择,可以将状态完全清空。

您可以看到涉及各种属性

而且这还不是全部。这是一个 Normalize 对按钮所做操作 的综合代码块。

button {
  font-family: inherit; /* For all browsers */
  font-size: 100%; /* For all browsers */
  line-height: 1.15; /* For all browsers */
  margin: 0; /* Firefox and Safari have margin */
  overflow: visible; /* Edge hides overflow */
  text-transform: none; /* Firefox inherits text-transform */
  -webkit-appearance: button; /* Safari otherwise prevents some styles */
}

button::-moz-focus-inner {
  border-style: none;
  padding: 0;
}

button:-moz-focusring {
  outline: 1px dotted ButtonText;
}

一致的 .button

除了 使用重置或基线 CSS 之外,您可能还需要一个按钮类,该类为您提供强大的样式基础并适用于链接和按钮。

.button {
  border: 0;
  border-radius: 0.25rem;
  background: #1E88E5;
  color: white;
  font-family: -system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  padding: 0.25rem 0.5rem;
  margin: 0.25rem;
  cursor: pointer;
}

查看这个 Pen 了解为什么所有这些属性都是必要的,以确保它在所有元素中都能正常工作。

按钮状态

与链接一样,您需要为按钮状态设置样式。

button:hover { }
button:focus { }
button:active { }
button:visited { } /* Maybe less so */

您可能还想 使用 ARIA 属性进行样式设置,这是一种鼓励正确使用 ARIA 属性的巧妙方法。

button[aria-pressed="true"] { }
button[aria-pressed="false"] { }

总有例外。例如,一个网站需要在句子中使用按钮触发的操作

<p>You may open your <button>user settings</button> to change this.</p>

我们在上面的代码中使用了按钮而不是锚点标签,因为这个假设的网站在模态对话框中打开用户设置,而不是链接到另一个页面。在这种情况下,您可能希望将按钮的样式设置为看起来像链接。

这可能足够罕见,以至于您可能需要创建一个类(例如 .link-looking-button)来包含上面提到的重置样式,并与您对锚点链接的处理方式相匹配。

突破按钮

还记得我们之前谈论过将整个元素包装在链接中的可能性吗?如果您在另一个元素中有一个按钮,但您希望整个外部元素都可点击/可触摸,就像它是按钮一样,那就是 一个“突破”按钮。您可以使用按钮上的绝对定位伪元素将可点击区域扩展到整个区域。很酷吧!

JavaScript 注意事项

即使没有 JavaScript,按钮元素也可以通过键盘上的 SpaceEnter 键触发。这正是它们如此吸引人和有用的元素的原因之一:它们是可发现的、可聚焦的,并且以可预测的方式与辅助技术交互。

也许在这种情况下,任何 <button> 都应该 JavaScript 插入 DOM。这是一个艰巨的任务!值得思考。🤔

“一次”处理程序

假设一个按钮执行了一些非常重要的操作,比如提交付款。如果按钮被编程为多次点击会提交多次付款请求,那将非常可怕。在这样的情况下,您将把点击处理程序附加到一个只执行一次的按钮上。为了让用户清楚这一点,我们也会在点击时禁用该按钮。

document.querySelector("button").addEventListener('click', function(event) {
  event.currentTarget.setAttribute("disabled", true);
}, {
    once: true
});

然后,您将有意地取消禁用按钮并重新附加处理程序,在必要时。

内联处理程序

通过在按钮本身的代码中激活按钮,可以执行 JavaScript 代码

<button onclick="console.log('clicked');">
  Log it.
</button>

<button onmousedown="">
</button>

<button onmouseup="">
</button>

这种做法从标准做法变成了失误(没有将 JavaScript 功能从 HTML 中抽象出来),然后变成了,呃,当你需要时你就需要它。一个优点是,如果您将此 HTML 插入 DOM,则不需要将 JavaScript 事件处理程序绑定/重新绑定到它,因为它已经有一个事件处理程序。

JavaScript 框架

在任何 JavaScript 框架中,通常都会为处理按钮创建一个组件,因为按钮通常有很多变体。这些变体可以转化为一种 API。例如,在 React 中

const Button = ({ className, children }) => {
  const [activated, setActivated] = React.useState(false);
  return (
    <button
      className={`button ${className}`}
      aria-pressed={activated ? "true" : "false"}
      onClick={() => setActivated(!activated)}
    >
      {children}
    </button>
  );
};

在这个例子中,<Button /> 组件确保按钮将具有 button 类并处理类似切换的活动类。

可访问性注意事项

按钮最大的可访问性考虑因素是实际使用按钮。不要尝试用 <div><span> 来复制按钮,这不幸地比您想象的更常见。这很可能导致问题。(您处理了可聚焦性吗?您处理了键盘事件吗?很好。您可能还忘记了一些其他东西。)

焦点样式

与所有可聚焦元素一样,浏览器会应用默认的焦点样式,通常是蓝色边框。

Chrome/macOS 上的焦点样式

虽然您可以争辩说您应该保留它,因为它对从焦点样式中获益的人来说是一个非常清晰明显的样式,但更改它也不无道理。

不应该做的是 button:focus { outline: 0; } 来删除它。如果您曾经删除过这种焦点样式,请在同一时间将其放回。

button:focus {
  outline: 0; /* Removes the default blue ring */

  /* Now, let's create our own focus style */
  border-radius: 3px;
  box-shadow: 0 0 0 2px red;
}
自定义焦点样式

按钮在点击时可能会变为焦点并同时应用该样式,这对于某些人来说很反感。有一种技巧(它对 浏览器支持有限,但正在增加)可以从点击中删除焦点样式,而不会影响键盘事件

:focus:not(:focus-visible) { 
  outline: 0; 
}

ARIA

按钮已经拥有了它们需要的rolerole="button")。但还有一些其他的ARIA属性与按钮有关。

  • aria-pressed:将按钮变成一个切换按钮,在aria-pressed="true"aria-pressed="false"之间切换。关于切换按钮的更多信息,也可以使用role="switch"aria-checked="true"实现。
  • aria-expanded:如果按钮控制着另一个元素(例如下拉菜单)的打开/关闭状态,你就可以使用这个属性来指示,例如aria-expanded="true"
  • aria-label:覆盖按钮内的文本。这对于标记没有文本的按钮很有用,但你可能仍然最好使用一个visually-hidden类,以便它可以被翻译。
  • aria-labelledby:指向一个元素,该元素将作为按钮的标签。

对于最后一个属性

<button aria-labelledby="buttonText">
  Time is running out! 
  <span id="buttonText">Add to Cart</span>
</button>

Deque 有一篇关于按钮可访问性的更深入的博客文章,其中包含了关于 ARIA 的很多内容。

对话框

如果一个按钮打开了一个对话框,你的工作就是将焦点移动到对话框内部并将其锁定在那里。关闭对话框时,你需要将焦点返回到该按钮,这样用户就可以回到他们开始的地方。这使得使用模态的体验对于依赖辅助技术的使用者和不依赖辅助技术的使用者来说都是一样的。

焦点管理不仅仅适用于对话框。如果点击一个按钮执行计算并更改页面上的一个值,那么就没有上下文更改,这意味着焦点应该保持在按钮上。如果按钮执行“移动到下一页”这样的操作,焦点应该移动到下一页的开头。

尺寸

不要把按钮做得太小。这适用于链接和任何类型的交互控件。任何有行动不便的人都会受益

苹果公司经典的触摸目标(按钮)的最小尺寸是44x44pt。

这里有一些其他公司的指南菲茨定律告诉我们,较小的目标会有更高的错误率。谷歌甚至在评估网站的 SEO 时会考虑按钮的尺寸

除了尺寸要充足之外,不要把按钮彼此靠得太近,无论是垂直堆叠还是在一行上并排排列。给他们留一些空白,因为行动不便的人可能会点击错误的按钮。

激活按钮

按钮可以通过点击/触摸、按下Enter键或按下Space键(当获得焦点时)来激活。即使你将role="button"添加到链接或 div 中,你也不会获得空格键的功能,所以即使是老生常谈,在这些情况下也要使用<button>