比较各种隐藏 CSS 内容的方法

Avatar of Marko Ilic
Marko Ilic

DigitalOcean 为您的旅程各个阶段提供云产品。立即开始使用 $200 免费信用额度!

您可能认为使用 CSS 隐藏内容是一个简单且已解决的问题,但实际上有多种解决方案,每种解决方案都独一无二。

开发人员 最常使用 display: none 来隐藏页面上的内容。不幸的是,这种隐藏内容的方式并非万无一失,因为现在该内容对屏幕阅读器 “不可访问”。虽然使用它很诱人,但尤其是在只有视觉上隐藏内容的情况下,不要使用它

事实上,CSS 中有许多方法可以“隐藏”内容,每种方法都有其优缺点,这些优缺点在很大程度上取决于它的使用方式。我们将在这里回顾每种技术,并在最后总结一下,帮助我们决定何时使用哪种技术。

如何区分这些技术之间的差异

为了看到不同隐藏内容方式之间的差异,我们必须引入一些指标。我们将使用这些指标来比较这些方法。我决定通过提出一些问题来分解这些指标,这些问题侧重于影响布局、性能和无障碍性的四个特定领域。

  1. 无障碍性:屏幕阅读器是否可以读取隐藏的内容?
  2. 文档流:隐藏元素是否会影响文档布局?
  3. 渲染:隐藏元素的盒子模型是否会被渲染?
  4. 事件触发器:元素是否检测点击或焦点?

现在我们已经确定了标准,让我们来比较这些方法。同样,我们将把所有内容放在最后汇总起来,我们可以用它作为参考,在隐藏 CSS 内容时做出决策。

方法 1:display 属性

我们在文章开头就警告过不要使用 display 来隐藏内容。正如我们所建立的,使用它来隐藏元素意味着该元素根本不会生成。它存在于 DOM 中,但实际上从未渲染过。

如果检查页面,该元素将继续显示在标记中。盒子模型不会生成或出现在页面上,这也适用于**所有子元素**。

更重要的是,如果该元素有任何事件监听器(例如点击或悬停),它们根本不会注册。正如我们已经讨论过的,所有内容都会被屏幕阅读器忽略。在这里,我们有两个可见按钮和一个使用 display: none 隐藏的按钮。所有三个按钮都有点击事件,但只有两个可见按钮会渲染并注册点击事件。

Display 是唯一影响图像请求触发的属性。如果一个图像标签(或父元素)的 display 属性设置为 none(通过内联 CSS 或选择器),那么图像将被下载。另一方面,如果图像使用 background 属性应用,那么图像将不会被下载

这是因为解析器在解析 HTML 文档时还没有应用 CSS,并且它遇到了一个 <img> 标签。另一方面,当我们使用 background 属性将图像应用于元素时,图像将不会被下载,因为解析器还没有应用调用图像的 CSS。这种行为在所有最新浏览器中都是一致的。唯一的例外是 IE 11,它将在两种情况下都下载图像。

指标结果
屏幕阅读器是否可以读取隐藏的内容?
隐藏元素是否会影响文档布局?
隐藏元素的盒子模型是否会被渲染?
元素是否检测点击或焦点?

方法 2:visibility 属性

如果元素的 visibility 属性设置为 hidden,那么该元素将被“视觉上隐藏”。“视觉上隐藏”听起来很像 display: none 的作用,但它与之大不相同,因为该元素会被生成和渲染,只是不可见。这意味着元素的盒子模型存在,它会占用屏幕上的空间,即使它看起来并不在那里。

想象一下你穿着一件隐形斗篷,使你对其他人不可见,但你仍然可以撞到东西。你的身体存在,即使你对人眼不可见。

但这就是“视觉上隐藏”和“不显示”之间差异的终点。事实上,使用 visibilitydisplay 隐藏的元素在无障碍性和事件触发器方面表现相同。不可见的元素对屏幕阅读器不可访问,并且不会注册事件,正如我们在下面的演示中看到的那样,它与上一个示例完全相同,只是将 display: none 换成了 visibility: hidden

指标结果
屏幕阅读器是否可以读取隐藏的内容?
隐藏元素是否会影响文档布局?
隐藏元素的盒子模型是否会被渲染?
元素是否检测点击或焦点?

方法 3:opacity 属性

opacity 属性只影响元素的视觉效果。如果我们将元素的 opacity 设置为零,则该元素将完全透明。同样,这非常类似于 visibility: hidden,我们将一件隐形斗篷披在元素上,使它不可见,但仍然存在。

换句话说,我们得到的是一个空心的透明元素,它像任何其他元素一样工作,只是它不可见。听起来很像 visibility 方法,对吧?区别在于完全透明的元素仍然可以被屏幕阅读器访问,并且可以注册事件,例如点击事件,正如我们在下面的示例中看到的。

指标结果
屏幕阅读器是否可以读取隐藏的内容?
隐藏元素是否会影响文档布局?
隐藏元素的盒子模型是否会被渲染?
元素是否检测点击或焦点?

方法 4:position 属性

使用绝对定位将元素推离屏幕是开发人员经常用来隐藏内容的另一种方法。使用 topleft,我们可以将元素推到屏幕外,这样它就永远不会被看到。这就像把饼干罐藏在房子外面,这样孩子们(或者也许是你!)就找不到它们。

“绝对”是这里关键词。如果我们将 position 设置为 absolute,则元素将从文档流中移除,这意味着它不再遵守其在 DOM 中的自然位置。换句话说,页面不会为它保留任何空间,这会使元素在视觉上失去顺序,如果存在,则将其定位到最近的定位元素,如果不存在,则定位到文档根元素。

我们利用绝对定位将“隐藏”元素从文档流中移除,并使用 -9999px 的值将其偏移到左上角。

.hidden {
  position: absolute;
  top: -9999px;
  left: -9999px;
}
指标效果
屏幕阅读器是否可以读取隐藏的内容?
隐藏元素是否会影响文档布局?
隐藏元素的盒子模型是否会被渲染?
元素是否检测点击或焦点?

如果隐藏的元素包含可聚焦内容,则页面会在该元素处于焦点时滚动到该元素,导致页面突然跳动。

方法 5:“视觉上隐藏”类

到目前为止,position 方法是我们在 CSS 中看到的最接近无障碍隐藏的方式。但可聚焦内容导致页面突然跳动的问题并不理想。另一种无障碍隐藏方法是结合绝对定位、clip 属性和隐藏溢出。Scott O’Hara 于 2017 年 在博客中介绍了它

.visually-hidden:not(:focus):not(:active) {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
}

让我们分解一下。

我们需要将元素从文档流中移除。最好的方法是使用 position: absolute。这将移除元素,但我们不会将其推离屏幕。

.visually-hidden {
  position: absolute;
}

我们可以通过将宽度和高度属性设置为零来隐藏元素。不幸的是,这行不通,因为一些屏幕阅读器会忽略宽度和高度为零的元素。我们可以做的是将其设置为第二低的值,1px。这意味着内容很容易溢出空间,因此我们还需要 overflow: hidden 来确保它不会在视觉上溢出。

.visually-hidden {
  height: 1px;
  overflow: hidden;
  position: absolute;
  width: 1px;
}

为了隐藏那个 1 像素的正方形,我们可以使用 CSS 的 裁剪属性。它非常适合这种情况,因为它不会影响屏幕阅读器。内容存在,但仍然是视觉上隐藏的。需要注意的是,clip 已被弃用,取而代之的是 clip-path,但如果我们需要支持旧版本的 Internet Explorer,它仍然是必需的。

.visually-hidden {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  width: 1px;
}

“视觉隐藏”类谜题的另一个部分是解决挤压在屏幕外可访问文本,这是一种奇怪现象,它会删除单词之间的空格,导致它们被朗读成一个大字符串。例如,“欢迎回家”将被读作“Welcomebackhome”。

解决此问题的一个简单方法是设置white-space: nowrap

.visually-hidden {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

最后!要考虑的最后一件事是允许某些具有本机焦点和活动站点的元素在它们获得焦点时显示,同时继续阻止其他元素(如段落)显示。我们可以使用:not伪选择器来实现。

.visually-hidden:not(:focus):not(:active) {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}
指标结果
屏幕阅读器是否可以读取隐藏的内容?
隐藏元素是否会影响文档布局?
隐藏元素的盒子模型是否会被渲染?
元素是否检测点击或焦点?

值得一提的是

除了我们介绍的五种方法之外,还有更多的方法。例如,text-indent属性可以像position方法一样将文本推离屏幕

.hidden {
  text-indent: -9999em;
}

不幸的是,这种方法与RTL写作模式不兼容。这使得它不如我们介绍的其他解决方案灵活。

另一种方法是使用transform来缩放或移动元素以使其离开屏幕。它与opacity的功能相同——只是视觉上的——。

.hidden {
  transform: scale(0);
}

让我们将所有内容整合在一起!

我们找到了一个可以视觉隐藏内容,但仍然可访问的解决方案。那么,你应该停止使用display: none吗?不,这仍然是完全隐藏元素(视觉上和可访问地)的最佳方法。

也就是说,值得一提的是,如果你想要实现相反的结果——从屏幕阅读器中隐藏内容,aria-hidden="true"属性将从屏幕阅读器中隐藏内容,但不会在视觉上隐藏。

因此,这里有一个完整的表格,比较了所有方法。当你下次遇到这种情况时,可以使用它来指导你如何隐藏内容的决定。

指标显示可见性不透明度位置可访问的方式
屏幕阅读器是否可以读取隐藏的内容?
隐藏元素是否会影响文档布局?
隐藏元素的盒子模型是否会被渲染?
元素是否检测点击或焦点?