禁用链接的话题前几天在我的工作中出现了。不知何故,去年在我没注意的时候,一个“禁用”锚点样式被添加到我们的排版样式中。但是,存在一个问题:在 HTML 中没有真正的方法可以禁用 <a>
链接(具有有效的 href
属性)。更不用说,你为什么要这样做?链接是网络的基础。
在某个时刻,我的同事似乎并不接受这个事实,所以我开始思考如何才能实现这一点。知道这需要付出很多努力,我想证明它不值得付出努力和代码来支持这种非常规的交互,但我担心通过展示它可以做到,他们会忽略我所有的警告,并只使用我的例子作为证明这样做是可以的。这件事对我来说还没有完全明朗,但我认为我们可以回顾一下我的研究。
首先,第一件事
不要这样做。
禁用的链接不是链接,它只是文本。如果你的设计需要禁用链接,你需要重新考虑你的设计。
Bootstrap 有将 .disabled
类应用于锚标记的示例,我为此讨厌它们。至少他们提到该类仅提供禁用的样式,但这具有误导性。如果你真的想禁用链接,你需要做的不仅仅是让它看起来已禁用。
万无一失的方法:移除 href
如果你决定忽略我的警告并继续禁用链接,那么删除 href
属性是我所知最好的方法。
来自官方的 超链接规范
a
和area
元素上的href
属性不是必需的;当这些元素没有href
属性时,它们不会创建超链接。
来自 MDN 的更容易理解的定义
此属性可以省略(从 HTML5 开始)以创建占位符链接。占位符链接类似于传统的超链接,但不会指向任何位置。
这是一个设置和移除 href
属性的基本 JavaScript 代码
/*
* Use your preferred method of targeting a link
*
* document.getElementById('MyLink');
* document.querySelector('.link-class');
* document.querySelector('[href="https://unfetteredthoughts.net"]');
*/
// "Disable" link by removing the href property
link.href = '';
// Enable link by setting the href property
link.href = 'https://unfetteredthoughts.net';
通过 CSS 对其进行样式设置也很简单
a {
/* Disabled link styles */
}
a:link, a:visited { /* or a[href] */
/* Enabled link styles */
}
这就是你需要做的!
这还不够,我想要更复杂的东西,这样我看起来更聪明!
如果你必须过度设计一些极端的解决方案,这里有一些需要考虑的事情。希望你会注意并认识到我即将向你展示的内容不值得付出努力。
首先,我们需要为我们的链接设置样式,使其看起来已禁用。
.isDisabled {
color: currentColor;
cursor: not-allowed;
opacity: 0.5;
text-decoration: none;
}
<a class="isDisabled" href="https://unfetteredthoughts.net">Disabled Link</a>

将 color
设置为 currentColor
应该将字体颜色重置回你的正常非链接文本颜色。我还将鼠标光标设置为 not-allowed
以在悬停时显示一个很好的指示器,表明不允许进行正常操作。我们已经排除了无法悬停的非鼠标用户,主要是触摸和键盘用户,因此他们不会获得此指示。接下来,不透明度降低到一半。根据 WCAG,禁用的元素不需要满足颜色对比度指南。我认为这非常冒险,因为它此时基本上是纯文本,并且将不透明度降低一半会使视力低下用户的阅读变得非常困难,这是我讨厌它的另一个原因。最后,删除文本修饰下划线,因为这通常是表明某物是链接的最佳指示器。现在这看起来像一个禁用的链接!
但它实际上并没有禁用!用户仍然可以点击/点击此链接。我听到你在大喊 pointer-events
。
.isDisabled {
...
pointer-events: none;
}
好了,我们完成了!禁用的链接实现了!除了,它真正只对点击的鼠标用户和点击的触摸用户禁用。不支持 pointer-events
的浏览器呢?根据 caniuse,Opera Mini 和 IE<11 不支持此功能。IE11 和 Edge 实际上不支持 pointer-events
,除非 display
设置为 block
或 inline-block
。此外,将 pointer-events
设置为 none
会覆盖我们漂亮的 not-allowed
光标,因此现在鼠标用户不会获得该链接已禁用的额外视觉指示。这已经开始瓦解了。现在我们必须更改我们的标记和 CSS……
.isDisabled {
cursor: not-allowed;
opacity: 0.5;
}
.isDisabled > a {
color: currentColor;
display: inline-block; /* For IE11/ MS Edge bug */
pointer-events: none;
text-decoration: none;
}
<span class="isDisabled"><a href="https://unfetteredthoughts.net">Disabled Link</a></span>
将链接包装在 <
span
>
中并添加 isDisabled
类为我们提供了禁用视觉样式的一半。这里一个不错的副作用是,禁用类现在是通用的,可以用于其他元素,如按钮和表单元素。实际的锚标记现在将 pointer-events
和 text-decoration
设置为 none
。
键盘用户怎么办?键盘用户将使用 ENTER
键激活链接。pointer-events
仅适用于指针,没有键盘事件。我们还需要阻止不支持 pointer-events
的旧版浏览器的激活。现在我们必须引入一些 JavaScript。
引入 JavaScript
// After using preferred method to target link
link.addEventListener('click', function (event) {
if (this.parentElement.classList.contains('isDisabled')) {
event.preventDefault();
}
});
现在我们的链接看起来已禁用,并且不会响应点击、点击和 ENTER
键的激活。但我们还没有完成!屏幕阅读器用户无法知道此链接已禁用。我们需要将此链接描述为已禁用。disabled
属性在链接上无效,但我们可以使用 aria-disabled="true"
。
<span class="isDisabled"><a href="https://unfetteredthoughts.net" aria-disabled="true">Disabled Link</a></span>
现在我将借此机会根据 aria-disabled
属性设置链接的样式。我喜欢将 ARIA 属性用作 CSS 的钩子,因为元素样式不正确是表明重要的辅助功能缺失的指标。
.isDisabled {
cursor: not-allowed;
opacity: 0.5;
}
a[aria-disabled="true"] {
color: currentColor;
display: inline-block; /* For IE11/ MS Edge bug */
pointer-events: none;
text-decoration: none;
}
现在我们的链接看起来已禁用、行为已禁用,并且被描述为已禁用。
不幸的是,即使链接被描述为已禁用,一些屏幕阅读器(JAWS)仍然会将其宣布为可点击的。它对任何具有点击侦听器的元素都这样做。这是因为开发人员倾向于将非交互式元素(如 div
和 span
)作为具有简单侦听器的伪交互式元素。我们在这里无能为力。我们为删除任何表明这是链接的指示而做的一切都被我们试图欺骗的辅助技术所破坏,具有讽刺意味的是,因为我们之前曾试图欺骗它。
但是如果我们将侦听器移动到主体怎么办?
document.body.addEventListener('click', function (event) {
// filter out clicks on any other elements
if (event.target.nodeName == 'A' && event.target.getAttribute('aria-disabled') == 'true') {
event.preventDefault();
}
});
我们完成了吗?好吧,还没有。在某个时候,我们需要启用这些链接,因此我们需要添加其他代码来切换此状态/行为。
function disableLink(link) {
// 1. Add isDisabled class to parent span
link.parentElement.classList.add('isDisabled');
// 2. Store href so we can add it later
link.setAttribute('data-href', link.href);
// 3. Remove href
link.href = '';
// 4. Set aria-disabled to 'true'
link.setAttribute('aria-disabled', 'true');
}
function enableLink(link) {
// 1. Remove 'isDisabled' class from parent span
link.parentElement.classList.remove('isDisabled');
// 2. Set href
link.href = link.getAttribute('data-href');
// 3. Remove 'aria-disabled', better than setting to false
link.removeAttribute('aria-disabled');
}
就是这样。我们现在有一个禁用的链接,对于所有用户来说,它在视觉上、功能上和语义上都已禁用。它只需要 10 行 CSS、15 行 JavaScript(包括主体上的 1 个侦听器)和 2 个 HTML 元素。
说真的,朋友们,不要这样做。
是的,在我看来,如果需要这样做,可能会有更好的方法来实现想要实现的目标。
同意。不确定为什么任何人在互联网上想要禁用链接。这就像关闭迪士尼乐园的所有游乐设施。
啊嗯。当然有道理。链接可能暂时不可用或在某些条件或时间段内不可用。坚持必须是链接很奇怪。一个禁用的按钮可以在不造成所有混乱的情况下解决问题。只需将其样式设置为内联链接!*放下麦克风*
(拿起麦克风)Samuel,使用按钮将是不正确的语义使用。链接用于导航/上下文更改,要么到完全不同的 URL,要么到另一个页面内位置,而按钮用于执行页面上的动态更改。两者都有其适当的用法,不应混淆,否则你会让依赖这些语义来理解如何交互以及交互结果的用户感到困惑。你应该查看 Marcy Sutton 的这个 链接与按钮 演示文稿以获取更多信息。
我理解——不要这么做,但为什么不只保留JS处理程序,去掉额外的标记,并使用.disabled或[aria-disabled] CSS选择器,而不用pointer-events属性呢?看起来效果还不错。
嗨,Stephen!我并不是想让它变得简单,但我确实希望尽可能全面和包容,为每个人提供相同的体验。正如我提到的,我在提供解决方案方面遇到了困难,但我希望每个人都能认识到这有多么荒谬,并且不要这样做。
实际上,这只是试图让人们思考,并可能学习一些他们之前不知道的关于HTML、CSS和ARIA的知识。
说实话,这样做简直是疯了。自从有网络以来,我就一直在制作网站,包括为企业客户制作网站。我从未需要过这个功能,也无法想象在现实生活中使用这种方法的实际案例。现在的孩子们……
同意。遗憾的是,这种情况经常出现。尤其是在开源库/框架中。希望现在的孩子们能注意并不要这样做。这不仅仅是开发人员的问题,也是设计问题。
我有一个用例。
在我的团队正在使用的课程平台中,平台会显示一些在完成前一课之前不应该访问的课程。
当你点击这些课程时,它会带你到一个页面,上面写着“你没有权限”。
将用户直接发送到错误页面并不是好的设计。因此,我覆盖了他们的代码以禁用链接。
嗨,Rocky,我真的很认为在你的情况下,移除href并创建一个占位符链接实际上是一个非常好的解决问题的方法。最终,这些不需要是链接,它们实际上只是静态文本。对于某些用户来说,他们期望以某种方式使用链接,而你的额外JS则阻止了他们预期的交互,却没有解释原因。
你有没有想过创建一个带有花哨的ES7语法的React组件,这样你绝对需要webpack+babel来禁用那些讨厌的链接?
/s
Dominic,这很有趣。我真是笑出声了。
如果你删除了HREF,就像你在文章开头描述的那样,那么锚点将不再可聚焦键盘,因此屏幕阅读器将无法“看到”它……
……或者我错了?
嗨,Basher,是的,删除HREF实际上会创建一个占位符链接。由于它不可操作,因此它不应该可聚焦。屏幕阅读器仍然可以访问它,它没有被隐藏。它不再可操作了。因此,一个“禁用”的链接。
iOS VoiceOver完全跳过无效链接(或者至少以前是这样),因此使用
href="#"
或类似的方法比完全删除href
更好。James,我认为这更像是一个用户代理缺陷,我不建议依赖它,这有点像黑客手段。最终,你只针对一小部分用户,而忽略了大量其他用户。
我同意不要这样做。但是设置
tabindex="-1"
而不是使用点击处理程序来preventDefault()
怎么样?这样链接就不能点击或键盘聚焦,然后屏幕阅读器应该不会朗读它。嘿,Corey,添加
tabindex="-1"
不会删除默认行为或语义,因此它仍然可操作(可点击)。它可能无法通过Tab键聚焦,但辅助技术仍然可以通过特殊的键盘快捷键或按类型列出元素的方式发现和操作它。没错,我并不是说它会删除默认行为或语义,我只是在回复这部分内容
因此,而不是使用JavaScript来
preventDefault()
,你可以使用tabindex="-1"。辅助技术不应该大声朗读任何内容或将该元素显示为交互式。嗨,Corey,
tabindex="-1"
会对辅助技术做一些其他事情,我不确定你是否会期望,并且可能会导致其他问题。只要元素上仍然具有链接语义,它就会对屏幕阅读器用户具有交互性和可发现性。添加tabindex="-1"
不会改变这一点。关于在你示例中为“启用”链接设置样式的一点说明
我建议删除
a
,在a:link, a:visited
中。它将特异性提高到11,而
:link, :visited
的特异性为10。特异性为11意味着它不会被经典的
.button
类覆盖,后者的特异性为10。此外,
:link, :visited
应该优先于[href]
,因为即使[href]
为空,它也会通过验证。嗨,Johan,感谢你的评论。最终,你需要使用适用于你实现的选择器,这只是为了说明重点。
不要这样做。知道了。
那么,假设有一个假设的遗留 Web 应用程序在有人阅读了像这样的博文并了解了更好的做法之前尝试使用这种策略……
推荐一些看起来至少大致相似的替代方案?场景是当前页面的业务规则限制链接,直到满足条件后才能使用,但仍然应该显示它们。显然,需要最少的JS和HTML来处理“链接”本身,但至少需要一行JS来切换状态(例如,切换CSS类)……同样的可访问性考虑因素。
想法?
如果你有一个元素有时是链接但目前不是,请使用没有
href
的占位符<a>
元素。你可以使用
data-*
属性来跟踪链接激活时将使用的URL。每个方向的变化都需要一行代码根据需要使用样式来区分
:link
和:not(:link)
元素。如果你正在制作一些需要加载后暂停的东西,以便强制观看者在点击之前阅读。我不知道,在处理一些更复杂的仪表板设计或简介时,我可以看到它的一些用途
我很好奇……这到底有什么实际用途?我想不出什么时候我想要某个东西看起来像链接但实际上不是链接。
并不是说你想要某个东西看起来像链接……
更多的是对于原本是链接的东西,但你需要用代码将其变成非链接。
评论中提到的两个用例是
1)(我的评论)当其他人的程序创建了一个链接,根据用户的状态,要么跳转到错误页面,要么跳转到正确的页面。我禁用了链接,直到状态正确。
2) 用于简介/教程,在其中禁用链接,直到用户“完成”某些操作。
你好,Gérard。补充Corey的评论,除了tabindex="-1",我还会添加aria-hidden="false"属性。这甚至会隐藏普通的旧链接。然后,包装SPAN标签可以具有一个aria-hidden属性,读取你想让它听起来像什么。例如,“Blah blah blah。禁用链接”。
做一些无用的事情只是为了证明我们能做到,这很有趣,哈哈。
……但是各位,不要在办公室尝试这个。在家试试吧,哈哈。
我能想到的唯一可能的原因是,禁用链接样式是为了显示链接现在已损坏……在这种情况下,你仍然希望它看起来像链接(只是稍微更改一下以表明它已损坏),并且用户是否可以点击它并不重要。
为什么不为移动用户使用
touch-action: none
?无论如何,你是对的,这很奇怪。您甚至都没有提到过(过时的)
<a name="..." >
标签,它曾经是链接目标的推荐用法。(URL中#后面的部分。)现在这些会被标记为禁用的链接,即使它们根本不是链接。尽管现在更常见的是使用
id
作为链接目标,但我了解到许多CMS仍然会插入<a name>
。这是一个潜在的用例……
我运营一个网站,其中包含指向我所有学生作品集网站的链接。由于各种原因,有时他们的网站会宕机(域名过期、主机过期、他们意外删除了网站等)。
我们鼓励行业和潜在雇主访问该网站。我不希望这些人访问一个损坏的网站。在这种情况下,我想在毕业生名单中保留学生的姓名,但在修复之前不链接他们的网站。我有一个WordPress插件,每24小时扫描一次损坏的链接,并且可以采取诸如向损坏的链接应用类等操作。
Alan Nugent 在留言中提到,可以将
tabindex="-1"
添加到链接中作为策略的一部分,因为链接不再可用,因此能够获得焦点可能也没有意义。