2
头图
本文参与了1024 程序员节活动,欢迎正在阅读的你也加入。

隐藏元素 display:none 与 visibility:hidden 的区别你真的知道吗?

有关隐藏元素的方式 display: none 与 visibility:hidden 的区别,这可以说是 css 面试题当中最常见的一道题了。相信大多数开发者被问到的第一答案就是:
display: none 不占据空间,visibility: hidden 占据空间。但实际上两者之间的区别并不只是不占据空间这么简单,且听我娓娓道来。

区别之一: visibility 拥有继承性

什么意思呢? 我们知道,如果一个元素的 visibility 设置为 hidden,它的子元素也会被隐藏,那么你知道子元素被隐藏的原理是什么吗?就是继承性。简单来说,就是子元素也继承了 visibility:hidden 的特性。换句话说,如果我们将子元素修改成 visibility: visible,你会发现子元素不再是隐藏,而是显示。来看一个例子感受一下:

html 代码如下:

<div class="vh-parent">
  <div class="vh-inherit-child">这是继承子元素的hidden隐藏</div>
  <div class="vv-child">子元素设置了visibility: visible之后又显示了</div>
</div>

css 代码如下:

.vh-parent {
  visibility: hidden;
}
.vv-child {
  visibility: visible;
}

实际运行效果如下图所示:

1.png

区别之二: visibility 不会影响 css 计数器

visibility:hidden 不会影响计数器的计数,这和 display:none 完全不一样。举个例子,如下 CSS 和 HTML 代码:

<ol>
  <li>列表</li>
  <li class="dn">列表</li>
  <li>列表</li>
  <li>列表</li>
</ol>
<ol>
  <li>列表</li>
  <li class="vh">列表</li>
  <li>列表</li>
  <li>列表</li>
</ol>
.vh {
  visibility: hidden;
}
.dn {
  display: none;
}
ol {
  border: 1px solid;
  margin: 1em 0;
  counter-reset: test;
}
li:after {
  counter-increment: test;
  content: counter(test);
}

实际运行效果如下图所示:

2.png

可以看到,visibility:hidden 虽然让其中一个列表不可见了,但是其计数效果依然存在。相比之下,设置 display:none 的列表就完全没有参与计数运算。

区别之三: visibility 过渡效果有效,而 display 则无效

CSS3 transition 支持的 CSS 属性中有 visibility,但是并没有 display。如以下示例:

/* 过渡效果无效 */
.test {
  display: none;
  position: absolute;
  opacity: 0;
  transition: opacity 0.25s;
}
.test:hover {
  display: block;
  opacity: 1;
}
/* 过渡效果无效 */
.test {
  position: absolute;
  opacity: 0;
  transition: opacity 0.25s;
  visibility: hidden;
}
.test {
  visibility: visible;
  opacity: 1;
}

由于 transition 可以延时执行,因此,和 visibility 配合可以使用纯 CSS 实现 hover 延时显示效果,由此提升我们的交互体验。来看如下一个示例:

<table>
  <tr>
    <td>数据1</td>
    <td>数据2</td>
    <td>
      <a href>操作▾</a>
      <div class="list">
        <a href>编辑</a>
        <a href>删除</a>
      </div>
    </td>
  </tr>
  <tr>
    <td>数据1</td>
    <td>数据2</td>
    <td>
      <a href>操作▾</a>
      <div class="list">
        <a href>编辑</a>
        <a href>删除</a>
      </div>
    </td>
  </tr>
</table>
td {
  padding: 5px 10px;
  border: 0;
  background: #fff;
  font-size: 14px;
}

td a {
  display: block;
}

.list {
  width: 80px;
  position: absolute;
  visibility: hidden;
  border: 1px solid #ccc;
  background: #fff;
}

td:hover .list {
  visibility: visible;
  transition: visibility 0s 0.2s;
}

.list a {
  padding: 5px 10px;
  color: #333;
}

.list a:hover {
  background-color: #f5f5f5;
}

效果如下图所示:

3.png

以上是一个很常见的 hover 悬浮显示列表效果,而且有多个触发点相邻,对于这种 hover 交互,如果在显示的时候增加一定的延时,可以避免不经意触碰导致覆盖目标元素的问题。如果没有增加延时效果,则会存在如下情况:我本来想去 hover 第二行的“操作”文字,但是由于鼠标光标移动路径不小心经过了第一行的“操作”,结果把第二行本来 hover 的“操作”覆盖了,必须重新移出去,避开干扰元素,重新 hover 才行。如此一来,对用户体验就不好了。而恰好 visibility 就可以处理这个问题。

区别之四: visibility 可以获得元素的尺寸位置,而 display 则无法获取

在实际开发中,我们会遇到这样的场景:我们需要对隐藏元素进行尺寸和位置的获取,以便对交互布局进行精准定位。这时候如果使用 display:none 来隐藏元素,我们获取到元素的尺寸位置则是 0,但是 visibility 则不会。js 代码如下:

console.log('clientWidth: ' + element.clientWidth);
console.log('clientHeight: ' + element.clientHeight);
console.log('clientLeft: ' + element.clientLeft);
console.log('clientTop: ' + element.clientTop);
console.dir(element.getBoundingClientRect());

因此面对以上的场景,我们更应该选择 visibility 来隐藏元素。

区别之五: visibility 在无障碍访问这一块比 display 更友好

视觉障碍用户对页面的状态变化都是通过声音而非视觉感知的,因此有必要告知其准确信息。

通过本文,我们了解到了 visibility 与 display 的详细区别,在面试的时候我们也不会只回答的片面了,知晓以上的区别能给我们的面试加分。

隐藏元素方式

最后分享一些隐藏元素的方式:

  1. 如果希望元素不可见,同时不占据空间,辅助设备无法访问,同时不渲染,可以使用<script>标签隐藏。例如:
<script type="text/html">
  <img src="1.jpg" />
</script>

<script>标签是不支持嵌套的,因此,如果希望在<script>标签中再放置其他不渲染的模板内容,可以试试使用<textarea>元素。例如:

<script type="text/html">
  <img src="1.jpg" />
  <textarea style="display:none;">
    <img src="2.jpg">
  </textarea
  >
</script>
  1. 如果希望元素不可见,同时不占据空间,辅助设备无法访问,但资源有加载,DOM 可 访问,则可以直接使用 display:none 隐藏。例如:
.hidden {
  display: none;
}
  1. 如果希望元素不可见,同时不占据空间,辅助设备无法访问,但显隐的时候可以有 transition 淡入淡出效果,则可以使用:
.hidden {
  position: absolute;
  visibility: hidden;
}
  1. 如果希望元素不可见,不能点击,辅助设备无法访问,但占据空间保留,则可以使用 visibility:hidden 隐藏。例如:
.hidden {
  visibility: hidden;
}
  1. 如果希望元素不可见,不能点击,不占据空间,但键盘可访问,则可以使用 clip 剪裁隐藏。例如:
.clip {
  position: absolute;
  clip: rect(0 0 0 0);
}
.out {
  position: relative;
  left: -999em;
}
  1. 如果希望元素不可见,不能点击,但占据空间,且键盘可访问,则可以试试 relative 隐藏。例如,如果条件允许,也就是和层叠上下文之间存在设置了背景色的父元素,则也可以使用更友好的 z-index 负值隐藏。例如:
.lower {
  position: relative;
  z-index: -1;
}
  1. 如果希望元素不可见,但可以点击,而且不占据空间,则可以使用透明度。例如:
.opacity {
  position: absolute;
  opacity: 0;
  filter: Alpha(opacity=0);
}
  1. 如果单纯希望元素看不见,但位置保留,依然可以点可以选,则直接让透明度为 0。例如:
.opacity {
  opacity: 0;
  filter: Alpha(opacity=0);
}
  1. 在标签受限的情况下希望隐藏某元素文字,例如:
.hidden {
  text-indent: -120px;
}
  1. 如果希望显示的时候可以加一个 transition 动画,可以使用 max-height 进行隐藏。
.hidden {
  max-height: 0;
  overflow: hidden;
}
注: 以上内容阅读《css世界》而整理。

夕水
5.2k 声望5.7k 粉丝

问之以是非而观其志,穷之以辞辩而观其变,资之以计谋而观其识,告知以祸难而观其勇,醉之以酒而观其性,临之以利而观其廉,期之以事而观其信。