原文链接:Hiding Elements On The Web,by Ahmad Shadeed
在平时的开发工作中,有时会有隐藏元素的需求。比如,一个按钮,需要在桌面端隐藏,在手机端显示;一个导航栏,需要在手机端隐藏,在桌面端显示。“隐藏”不是字面上展示的这样简单,它还包含几层意思:
- 元素完全从文档流中移除的隐藏。
- 元素仅仅是视觉上的隐藏,使用像屏幕阅读器(screen reader)这样的辅助技术(assistive technology,简称“AT”)依然是能访问的。
- 元素在视觉上可见,不过对屏幕阅读器是隐藏的,即屏幕阅读器是无法访问的。
通过本篇文章,你将会学到利用 HTML/CSS 隐藏元素的方法,内容涵盖了可访问性、动画和使用案例。我们一起来看看吧!
HTML5 hidden 属性
这是在 HTML 标签上使用的一个布尔值属性。在浏览器加载页面的时候,使用 hidden 属性修饰的元素,渲染效果与 display: none 类似。当然,如果使用 CSS 手动重写了 hidden 属性的话,就另当别论了。
来看下面的例子:
这部分包含了一个标题、图片和描述信息。图片只会在视口宽度大于 400px 的时候才显示。这里我给 <img> 加了一个 hidden 属性。
我写了一段 CSS 代码,让 hidden 属性修饰的图片在视口宽度大于 400px 的时候显示。
img[hidden] {
display: none;
}
@media (min-width: 400px) {
img[hidden] {
display: block;
}
}
下面是在大视口(大于 400px)下的渲染效果。
你可能要问了,为什么不直接使用 display: none 呢?好问题。图片使用 hidden 属性控制带来的好处是——即使 CSS 由于某种原因没有加载成功,图片也会隐藏。
hidden 属性对可访问性的影响
hidden 会将元素完全从页面隐藏,所以屏幕阅读器是无法访问。如果只是出于视觉表现的目的,一定要避免使用。
CSS display 属性
每个元素都有一个默认的 display 值,可能是 inline-block、block、table 等等。我们使用 display: none 就能达到隐藏元素的效果,并且该元素的所有后代都和它一起隐藏了。
与上面代码类似,不过这次我们使用了 display: none:
img {
display: none;
}
@media (min-width: 400px) {
img {
display: block;
}
}
display: none 会将元素从文档流中移除,屏幕阅读器也是无法访问到。那么,什么是文档流?我们可以用下面的图片来生动地描述它:
我们把蓝皮书用 display: none 隐藏了,最后发现它从这摞书里完全消失了,就好像被抽走了一样。蓝皮书原来占据的空间不存在了。HTML 于此类似,说明文档流被改变了。
下面的动画,展示了蓝皮书被移除时的状况:
图片隐藏后,还会加载吗?
会的。举个例子,<img> 默认使用 CSS 隐藏,我们将它设置在某个断点处显示。我们打开 DevTools,检查 networks 选项卡,还是发现图像被加载了,即使没有显示。
<style> 元素
值得一提的是,某些元素默认就是 display: none 的。比如 <style> 。我们可以把插入到 HTML 页面中的 <style> 的 display 值设置为 block,这样就能看见它了。
<body>
<style>
.title { color: #000; }
</style>
</body>
style {
display: block;
}
这事如果在让样式块(style block)变得可编辑,就更有趣了。可以通过为 <style> 添加 contenteditable 属性来实现。
Demo(译注:在 Firefox 中能看到上面的效果,而在 Chrome 里是看不到的)
display:none 对可访问性的影响
跟 hidden 属性一样,display: none 元素也是从页面中完全隐藏的,屏幕阅读器也无法访问。
opacity
元素设置 opacity 后,包括后代都会被隐藏,这不是因为继承的原因。然而,这只是视觉上的隐藏。还要注意的是,opacity 值小于 1 的元素会创建一个 层叠上下文。
上图里,蓝皮书只是在视觉上隐藏了。它所占据的空间仍然保留,与 display: none 效果比较的话,这一摞书的顺序没有发生变化。
img {
opacity: 0;
}
我们把最开始的例子,用 opacity: 0 重新改写下,结果会是这样的:
图片从视觉上隐藏了,但它占据空间依然是在的。
Dusan Milovanovic 指出,pointer-events: none | auto 可以用来禁用使用 opacity: 0 隐藏的元素上的鼠标事件。这还是很重要的,如果一个元素看不见了,但依然能响应用户的点击、悬停或选择文本的行为,勘定是很奇怪的。
opacity: 0 对可访问性的影响
使用 opacity: 0 隐藏的元素依然能被屏幕阅读器访问,也能被键盘聚焦。
visibility
使用 visibility: hidden 的隐藏元素与使用 opacity: 0 的元素类似,不影响视觉上的文档流表现。
请注意,蓝皮书从可视流中隐藏了,但并没有影响这摞书的顺序。
有一点,如果 visibility: hidden 是在父元素身上使用,那么它及其它的后代默认都是看不见的。但是,如果有一个子元素上使用了 visibility: hidden,那么这子元素将是可见的。
<article>
<h1>Spring is on the way</h1>
<img src="landscape.jpg" alt="">
<p><!-- Desc --></p>
</article>
article {
visibility: hidden;
}
img {
visibility: visible;
}
上例中,<article> 使用了 visibility: hidden,而子元素 <img> 是 visibility: visible 的,结果图片依然是显示的。也就是说,子元素是可以重写父元素的 visibility 属性的。
visibility: hidden 对可访问性的影响
如果一个元素 visibility: hidden 了,那么它以及它的所有后代元素都会从访问树(accessibility tree)中删除,不会被屏幕阅读器读到。
CSS position 属性
使用 position 属性将隐藏元素的原理,就是把元素移动到屏幕之外,设置其尺寸为 0 (宽和高)。比如,网页里 跳过导航(skip navigation) 链接:
为了让链接定位到屏幕之外,我们可以这么做:
.skip-link {
position: absolute;
top: -100%;
}
-100% 会将元素网上推出一个视口高度的距离。结果,链接就被完全隐藏了。当链接被键盘聚焦时,则可以这样设置:
.skip-link:focus {
position: absolute;
top: 0;
}
position: absolute | fixed 对可访问性的影响
元素可以被屏幕阅读器读到,被键盘聚焦。只是移动到视口之外了而已。
clip-path
clip-path 属性用来创建一个裁剪区域,只有在这个区域内的元素内容才是可见的,其他部分则是隐藏的。
上面的图片经过裁剪,两边的透明黑色区域看不见了。
为了用更直观的方式演示,我使用 clippy 工具来解释。在下面的 GIF 图中,我定义了如下的 clip-path:
img {
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
}
将多边形每个点的坐标设置为 (0, 0),则裁剪区域变为 0。结果,图像不会显示了。同样,还可以用一个圆(circle)来代替这里的多边形(polygon):
img {
clip-path: circle(0 at 50% 50%);
}
使用 clip-path 实现的隐形效果只是视觉上的,屏幕阅读器依然可以访问,键盘也能聚焦。
操作 color 和 font-size
尽管这两种技术并不像我们前面讨论的那样普遍,但在某些场景下比较有用。
color: transparent
将文本设置成透明色(transparent),只是视觉上隐藏了。这比较适合仅带图标的按钮。
font-size: 0
将文字大小设置为 0 也是视觉上的隐藏。
来看一个包含如下结构的按钮:
<button>
<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="false" focusable="false">
<!-- Path data -->
</svg>
<span>Like</span>
</button>
我们的目标是以一种能被访问的方式隐藏文本。为此,我使用了下面的 CSS:
.button span {
color: transparent;
font-size: 0;
}
文本隐藏了。
aria-hidden
当为元素添加 aria-hidden 后,就会从访问树中删除,这可以用来提升屏幕阅读器用户的体验。需要注意的是,元素依然是视觉可见的。
<button>
Menu
<svg aria-hidden="true"><!-- --></svg>
</button>
这里是一个带 label 和图表的菜单按钮。为了让 svg 对屏幕阅读器隐藏,这里添加了 aria-hidden。
根据 MDN 文档,aria-hidden 的使用场景包括:
- 用来隐藏修饰性内容,比如图标、图片。
- 隐藏重复的文字。
- 隐藏屏幕外的或折叠的内容。
aria-hidden 是为屏幕阅读器设计的,因此它仅对屏幕阅读器隐藏内容。但是,内容对于视觉用户仍然可见,并且也支持键盘聚焦。
动画和交互
速查表
在我们开始示例之前,我想带大家回顾一下前面提到的属性,我从 CSS-tricks 上的 这篇文章 获得了灵感,做了下面的一张速查表,方便大家在使用时根据需要选择合适的方法。
当我们想对隐藏元素使用动画的时候。比如,显示隐藏的移动导航,需要以一种可访问的方式来实现。为了获得访问性体验,我们将探索一些值得学习的好例子,以及一些不好的例子,避免犯错误,从而给屏幕阅读器用户带来更好的体验。
菜单动画 - 不好的例子
我们有一个菜单,在展开时使用一个划入的动画。最简单的做法是使用下面的方法:
ul {
opacity: 0;
transform: translateX(100%);
transition: 0.3s ease-out;
}
ul.active {
opacity: 1;
transform: translateX(0);
}
根据这种方法,菜单在添加 .active 类的时候显示,否则折叠。这个类是通过 JavaScript 添加的,如下所示。
menuToggle.addEventListener('click', function(e){
e.preventDefault();
navMenu.classList.toggle('active');
});
结果看起来不错,但它有一个大问题。使用 opacity: 0 的方式不会将导航从访问树中删除 。即便导航在视觉上隐藏了,可它仍然能被键盘聚焦,而且也能被屏幕阅读器读到。
下面是来自 Chrome DevTools 的访问树截图:
下面的截图,则是 Mac OS 上的访问工具 VoiceOver 看到的页面内容。跟上面一样,
简而言之,访问树是屏幕阅读器用户可以访问的所有内容的列表。在我们的例子中,包含一个导航列表,虽然它视觉上是隐藏的,但还是出现在了访问树中。因此,隐藏菜单时我们需要解决两个问题:
- 不能被键盘聚焦
- 不能被屏幕阅读器访问
菜单动画 - 好的例子
为了解决上面的文问题,我们需要在菜单导航上使用 visibility: hidden。这确保菜单不仅在视觉上是隐藏的,屏幕阅读器也无法访问。
ul {
visibility: hidden;
opacity: 0;
transform: translateX(100%);
transition: 0.3s ease-out;
}
ul.active {
visibility: visible;
opacity: 1;
transform: translateX(0);
}
添加后,现在菜单对屏幕阅读器也是隐藏的了。让我们再来测试一下 VoiceOver 的结果:
自定义复选框
默认的复选框设计很难自定义。因此,我们需要为复选框设计自定义样式。一般会这么做:
<p class="c-checkbox">
<input class="sr-only" type="checkbox" name="" id="c1">
<label class="c-checkbox__label" for="c1">Custom checkbox</label>
</p>
要自定义复选框,需要以可访问的方式隐藏输入框。为此,需要借助 position 等其他属性来实现。有一个常见的CSS 类,称为 sr-only 或 visual-hidden,用来在视觉上隐藏一个元素,但键盘和屏幕阅读器依然能访问。
.sr-only {
border: 0;
clip: rect(0 0 0 0);
-webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
white-space: nowrap;
}
这样,就可以访问自定义复选框。如果你想了解更多,我写了关于这个主题的 一篇文章。
对屏幕阅读器隐藏内容
在标题中,我使用了一个表情符号。如果隐藏,那么屏幕阅读器会使用下面的方式阅读:
Hiding On The Web grinning face with open mouth
每个表情符号都有一个对应的特定描述,屏幕阅读器阅读时会使用这个描述。想象你现在正在浏览一个网页,突然听到这个标题,读到后面,可能就有点懵逼了。为了避免这种混淆,可以使用 aria-hidden,把表情包设定为对屏幕阅读器隐藏。
<h1>Hiding On The Web <span aria-hidden="true">😃</span></h1>
小故事,大道理小改变,大胜利!
隐藏按钮
在 Twitter 上,有一个名为“See New Tweets”的按钮,默认使用 aria-hidden 对屏幕阅读器隐藏,只在有新推文可才会显示。
隐藏修饰性的内容
用户 ID 和日期之间的点是装饰性的。因此,使用了 aria-hidden="true" 避免被屏幕阅读器读到。
相关文章
- Toggle Visibility When Hiding Elements
- Hiding Things with CSS
- Places it’s tempting to use
display: none;, but don’t - Exploring how to build an accessible checkbox
- Accessible Skip Navigation Link
- See No Evil: Hidden Content and Accessibility
(完)
广告时间(长期有效)
我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。
瞄~