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

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

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

区别之一: visibility 拥有继承性

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

html 代码如下:

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

css 代码如下:

css 复制代码
.vh-parent {
  visibility: hidden;
}
.vv-child {
  visibility: visible;
}

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

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

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

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>
css 复制代码
.vh {
  visibility: hidden;
}
.dn {
  display: none;
}
ol {
  border: 1px solid;
  margin: 1em 0;
  counter-reset: test;
}
li:after {
  counter-increment: test;
  content: counter(test);
}

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

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

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

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

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

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

html 复制代码
<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>
css 复制代码
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;
}

效果如下图所示:

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

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

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

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>标签隐藏。例如:
html 复制代码
<script type="text/html">
  <img src="1.jpg" />
</script>

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

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

注: 以上内容阅读《css世界》而整理。

相关推荐
腾讯TNTWeb前端团队3 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom8 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom8 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom8 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试