Web隐藏术

发布于 大漠

在Web页面或Web应用程序的开发中,有些元素是需要被隐藏起来。让一个元素隐藏起来的实现方案会有很多种,比如说在《图片替换文本CSS方法》一文中所聊的图片替代文本的方案都适合于元素的隐藏,只不过每种不同的技术方案实现的原理和最终呈现给用户的渲染方式会有所不同。如果你是一个有情怀的前端开发者的话,在开发应用的时候很多时候还会考虑无障碍(可访问性)设计相关的细节。那么隐藏元素的不同技术手段对读屏软件之类的也是有所不同的。在这篇文章中,我们就一起来聊聊Web中隐藏术。

能想到的Web隐藏术

如果你阅读了《图片替换文本CSS方法》一文的话,你至少想到了十多种隐藏Web元素的姿势:

  • 设置display的值为none,这种方法也被称为**FIR方法**
  • margin值(一个足够大让元素移出视窗外的负margin值)
  • text-indent值(text-indent具有一个足够大的负值时,可以达到隐藏文本的效果)
  • 设置heightfont-sizeline-height值为0,让元素在视觉上不可见
  • CSS的clip让元素在视觉上不可见
  • position:absolute配合任何一个足够大的leftrightbottomtop的负值,让元素不在视窗范围内显示
  • visibility设置值为hidden在视觉上让元素不可见
  • opacity设置值为0在视觉上让元素不可见
  • clip-path让元素在视觉上不可见
  • HTML元素添加hidden属性,让元素不可见
  • filteropacity()取值为0在视觉上让元素不可见
  • mix-blend-mode在高亮模式下,让视觉上元素不可见

你应该还能想到有类似的其他方式,让一个Web元素真的或假的(视觉上)隐藏起来。

Web隐藏术的差异

上述提到的那些技术手段都可以让Web元素隐藏。但它们在Web中的具体渲染还是有所差异性的,如果您想彻底的掌握应该在什么样的场景之下采用哪种隐藏术,那么就有必要先掌握这些Web隐藏术在具体的渲染中的差异性。而要了解他们之间的差异性,我们可以围绕着下面这五个方面来展开或探讨:

  • 是否生成盒模型?
  • 该盒模型是否影响布局?
  • 该盒子在屏幕上可见吗(视觉上可见)?
  • 元素的内容会被屏幕阅读器读取吗?
  • 元素是否可操作(可单击,可聚焦)吗?

如此一来,接下来我们就来围绕着这五个方面来深入的了解Web隐藏术。

display

在CSS的世界中,HTML中的任何一个元素都是一个矩形框,即 盒模型。可以通过display的值来改变盒模型在Web页面中的渲染模式。使用display设置为none值时可以隐藏元素,不会产生这个盒模型。

尽管元素仍然在HTML标记中(如果使用浏览器开发者工具查看到元素),但实际上它并不存在于页面中。盒模型的任何部分(内容区域填充区域边框区域外距区域)都不会生成或出现在页面上。

设置了display: none的元素以及其后代元素中的任何内容在功能上都不存在。如果它是一个可操作的元素,例如buttona,则不能对其进行操作。元素和其中的任何内容也会被屏幕阅读器忽略。

围绕着前面提到的五个点,填个表格:

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
display: none

hidden属性

HTML5的hidden属性提供了一个语义指示器:

所有HTML元素都可能有hidden内容属性集。hidden属性是一个布尔属性。当在元素上指定时,它表示该元素还不相关或者不再相关。用户代理不应该渲染具有指定hidden属性的元素。

简单地说,在HTML元素上,我们可以显式的设置hidden属性,如果元素上具备hidden属性,它就不会被显示

规范中定义的后一部分很有趣,因为这表明你可以使用hidden属性将内容放在页面中,然后通过JavaScript访问该内容,以便在其他地方使用。例如,在使用了hidden<textarea>元素。另外,在使用hidden属性时,有一些事情我们是需要有所了解的,正如规范中所描述的:

  • 不应该使用hidden来隐藏应该可以在不同大小的屏幕、分辨率等上访问的内容
  • 不应该使用hidden来隐藏选项卡组件(Tab)的不可见部分或类似的内容切换器
  • 非隐藏元素不应该超链接到hidden元素上
  • 标记为hidden的元素仍然可能是活动的。例如,表单控件甚至<script>元素仍然是功能性的

事实上,HTML5的hidden属性带来的效果:

  • 在支持的浏览器中,内容不可见(用户看不到,不会在屏幕上显示)
  • HTML代码状态语义指示器
  • 客户端代理样式:display: none
  • 使用tab无法获取到焦点
  • 不会被包括在可访问性树中

在使用hidden属性时,在易访问性上有相应的映射属性。它被映射到aira-hidden="true",但它的行为与aria-hidden="true"不同。@Steve Faulkner在他的博客中做过相关的解释,详细解释了许多的不同之处。当然,最值得注意的是,具有aira-hidden="true"的元素在浏览器中仍然可见,但对于辅助技术则不可见(比如屏幕阅读器就会忽略),而且hidden的元素在任何地方都无法访问。

在@Steve Faulkner的文章中还解释了如何正确的使用hidden属性:如果你想对所有用户隐藏内容,请使用HTML5的hidden属性。没有必要使用aria-hidden属性

有关于aria方面更详细的介绍,可以阅读《WAI-ARIA 无障碍Web规范》一文。

接下来,回到我们的主题中来,设置了hidden属性的元素正如前面所描述的一样,元素不可见。如果你使用浏览器开发者工具查看代码的话,会发现对应的display值为none

hidden的值在truefalse之间切换时,元素在屏幕上隐藏和显示之间会进行切换。

仔细对比,它和display:none的表现是相似的:

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
hidden="true"
hidden="false"

同样的,显式设置了hidden的元素,可以显式的设置display为非none的值,即可让元素可见。

visibility: hidden

在CSS中,如果显式的给元素设置了visibility属性的值为hidden的话,那么该元素或其后代元素将会不可见(不会在屏幕上可视)。但它和display:nonehidden有所不同,仍然会生成盒模型。由于生成了盒模型,所以构成盒模型的四个区域将会影响页面其余部分的布局。然而这四个区域在屏幕上是不可见的

使用visibility:hidden的元素除了生成了盒模型之外,其他的特性和display:none类似。我们不能对该元素进行操作,屏幕阅读器也会忽略该元素。

如果使用浏览器开发者工具查阅设置了visibility:hidden的元素,不难发现,元素虽然不可见,但其在布局中还是占有相应的区域(盒模型区域依旧在屏幕上):

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
visibility:hidden

opacity: 0

opacity在CSS中主要是用来设置元素的透明度的。其值是0~1之间的一个值(小于0会以0渲染,大于1会以1渲染),该值越趋近于0时,元素在视觉上就越不可见。

当元素的opacity的值设置为0,元素在屏幕上不可见(屏幕上隐藏起来),但在功能上与将元素设置为任何纯色没有什么不同。元素仍然像往常一样占用布局的空间,屏幕阅读器同样能读取到,而且同样具有交互可操作性。

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
opacity:0

filter属性中的opacity()函数和opacity类似,通过该函数也可以控制元素的透明度,从而让元素在屏幕上是否可见。

如果使用opacity:0来隐藏元素,在屏幕阅读器和pointer-events的使用上会存在一定的缺陷。@Zell Liew在他的博文《A new (and easy) way to hide content accessibly》围绕着这方面有过深入的讨论。

position

position属性通常不用来隐藏元素。但在某些情景之下,需要在页面上可视化的隐藏一个元素,并且不影响页面布局时,使用position(一般取值absolute)属性配合topleft的值(一个较大的值,可以是正值,也可以是负值)达到元素可视化隐藏。

简单地说,position值为absolute结合lefttop等值可以将某个元素脱离文档流(让该元素不在视窗可视区域)。从而达到视觉上的不可见。另外,使用该方法来隐藏某个元素时,一般在该元素的祖先容器上设置overflow:hidden

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
position:absolute

在实际开发中,有的同学会使用margin的负值(足够大的一个负值)将元素移出屏幕可视区域,从而达到元素不可见。和position:absolute不同的是,设置负margin的元素在布局中还是会占用布局空间,从视觉上看上去有点类似于opacity: 0visibility:hidden的效果。

clip 和 clip-path

首先声明,clip已经从Web标准中已移除,使用clip-path来替代,但有些为了兼容较低版本IE浏览器时,还是会使用clip属性,另外,在使用clip属性的元素一定是要一个显式设置position为非static(和relative)。更详细的可以阅读《Clip》一文。

在A11y提供的一个经典的隐藏元素的方案使用的就是clipclip-path结合在一起:

.sr-only {
    position: absolute;
    height: 1px;
    width: 1px;
    clip: rect(1px 1px 1px 1px);
    clip: rect(1px,1px,1px,1px);
    clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
    -webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
    overflow: hidden !important;
}

如果忽略clip的使用,那么未显式的设置position:absolute时。或者说,仅设置clip-path属性,该元素也是不可见:

仅视觉上的效果来看,和visibility:hiddenopacity:0并无太大的差异化。

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
clip-path

如果你从未接触过clip-path,而且你又对这方面感兴趣,可以点击这里进行了解

mask

既然clip-path可以让元素不可见,那么CSS Masking Module Level1中的另一部分Masking中的相关特性应该也可以达到在视觉上隐藏元素的效果。

简单地说,CSS的mask也可以让某元素在视觉上不可见。

有关于CSS的mask在这里不做过多表述,感兴趣的可以阅读前几篇教程:《探索CSS Masking模块:Clipping》、《探索CSS Masking模块:Masking》和《Clipping和Masking 何时使用》。

如果你阅读了前面的系列教程的话,就知道mask用来遮盖元素(即让元素可见或不可见)有两种模式:高亮模式Alpha通道模式

这样一来,只需要在隐藏的元素上添加mask-image属性,设置一个全透明的背景图片,就可以让元素在视觉上不可见:

.is-mask {
    mask: linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0));
}

能达到类似clip-pathvisibility:hiddenopacity:0的效果:

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
mask

改变视觉尺寸

前面也提到了,HTML中任何一个元素都是一个矩形盒子,CSS中盒模型相关的属性用来描述该盒子所有。如果我们显式的将盒子尺寸都设置为0的时候,在视觉上,该元素就会不可见。

另外,max-height也能达到类似的效果。将max-height设置为0可以让元素视觉上不可见。而且很多场景会借助max-height0至一个显式的值,比如1000px,让该元素有一个展开的动效。当然之种动效不是最佳的效果,但在隐藏元素的效果上还是不错的。

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
改变盒型尺寸

transform

前面提到过margin可以将元素移出视窗可视区域,从而达到元素不可见的效果。CSS中的transformtranslate()也可以达到类似的效果。除此之外,还可以通过scale()函数达到类似的效果,将该函数的值设置为0,即缩小至不可见。从而达到元素不可见的效果。

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
transform

简单地小结一下

前面简单的介绍了九种不同的方式来隐藏Web上的元素,而且围绕着五个不同方面来展开,将这些方式整合成一个更详细的表格,大家在实际使用的时候可以有一个更好的参考:

隐藏术 是否有盒模型 是否影响布局 屏幕上是否可见 屏幕阅读器是否可读 元素是否可操作
display: none
hidden="true"
visibility:hidden
opacity:0
position:absolute
clip-path
mask
改变盒型尺寸
transform

而且下面几种方式:

  • opacity: 0
  • visibility:hidden
  • clip-path
  • mask
  • 改变视觉尺寸
  • transform

从视觉效果上可以让元素不可见,但元素还是会占据布局的空间。如果真正的想让元素不可见,而且不占用任何布局空间,那么在这几种方式中添加position: absolute。这样一来就可以让元素脱离文档流,从而不影响页面的布局。

Web隐藏元素的使用场景

可以说,上面聊的都是一些理论方面的东西,如何使用HTML5或CSS的特性来让元素不可见(隐藏)。那么在实际中,什么场景下来使用Web隐藏术呢?

图像替换文本

在Web使用中,常常会碰到图片替换文本的场景,比如说网站的Logo、Web图标等。而大部分同学可能会直接在一个空标签上使用background-image,比如:

<h1 class="logo"></h1>

.logo {
    background: url(logo.png)
}

从视觉效果上来看,并没有什么差异。你还原了所需要的UI效果。但如果在可访问性(无障碍设计)上深究的话,上述的方案并不是一个较好的方案。就拿屏幕阅读器来说,并读不到上面的内容。如果我们把上面提到的元素隐藏的技术运用到这里,那么效果会变得更好:

<h1 class="logo">
    <span class="sr-only">W3cplus!记述前端那些事,引领Web前沿,打造精品教程!</span>
</h1>

.logo {
    background: url(logo.png)
}

.sr-only {
    // 前面提到的任何一种可隐藏元素的方式都可行
}

好此一来,不仅还原了UI效果,还能在可访问性上更佳(屏幕阅读器可以读到.sr-only中的内容)。

目前,sr-only常用的方式是clip-path

.sr-only {
    position: absolute;
    height: 1px;
    width: 1px;
    clip: rect(1px 1px 1px 1px);
    clip: rect(1px,1px,1px,1px);
    clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
    overflow: hidden !important;
}

如果想让元素获得焦点时,隐藏的元素显示式出来,比如说使用Tab键,定位到隐藏的元素显示出来。我们需要对上述的选择器做一个小小调整:

.sr-only:not(:focus):not(:active) {
    position: absolute;
    height: 1px;
    width: 1px;
    clip: rect(1px 1px 1px 1px);
    clip: rect(1px,1px,1px,1px);
    clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
    overflow: hidden !important;
}

要是没使用sr-only来隐藏元素,而是采用aira-hidden="true"来隐藏元素的话,可以考虑@Nicolas Hoffmann提供的方案

转场动画

前段时间@张鑫旭老师的《你用的那些CSS转场动画可以换一换了》一文中重新介绍了一些实现转场动效的技术方案。事实上,我们可以把隐藏元素的一些技巧运用进来,也可以让转场动效变得更为有意思。正如@张鑫旭 老师教程中提到的clip-pathmask相关特性,你会发现,这两个特性也可以被运用于隐藏元素中。

除了教程中提到的案例,类似的转场效果还很多,比如这里提供的一些案例效果

总结

这篇文章主要和大家一起探讨了Web中的隐藏术。虽然隐藏元素的方式有很多种,但总结起来就是三大类:

  • 完全隐藏
  • 视觉上隐藏
  • 只是在辅助技术上隐藏

虽然这些方式都可以达到元素隐藏(或显式)之间的切换,但每种方式都有自己的利弊,在实际使用的时候还是需要根据运用场景来选择:

  • 使用CSS或[hidden]完全隐藏内容
  • 使用像.sr-only可视化隐藏内容,但对辅助技术可用(不隐藏)
  • 使用aria-hidden="true"让屏幕阅读器忽略内容

扩展阅读