html元素隐藏的实现方案

更新日期: 2019-08-05阅读: 2.5k标签: 元素

老生常谈之display: none

相信小伙伴们都被问过这样一个问题:让一个元素隐藏起来,有多少种方法呢?常规来讲,我们有三种方法display: none、opacity: 0和visibility: hidden ,基于display: none的副作用,已经是个被说烂的问题,主要是有以下缺点:

一、切换显隐时会导致reflow(回流),从而引起repaint(重绘),当页面中reflow增多至一定程度时,会导致cpu使用率飙高。

二、无法对元素设置过渡动画,也无法进行方位测量(包括clientWidth, clientHeight, offsetWidth, offsetHeight, scrollWidth, scrollHeight, getBoundingClientRect(), getComputedStyle())

原因是:浏览器会解析html标签生成dom Tree,解析css生成CSSOM,然后将DOM Tree和CSSOM合并生成Render Tree,最后才根据Render Tree的信息布局来渲染界面,但设置了display: none的元素,是不会被加入Render Tree中的,自然也无法渲染过渡动画。

三、用它来设置显隐切换时,会因为与display: flex、display: grid冲突而使人困扰。


你真的了解opacity和visibility吗

如此说来,我们设置元素显隐时,使用opacity或visibility似乎是更好的选择,但小伙伴们有没有考虑过,opacity: 0和visibility: hidden 这两者又有何具体区别呢?
既然标题写着寻根问底,那么我们就通过几轮PK来深挖一下这两者的具体区别:

第一轮:动画属性

常见的动画效果中,使用最广泛的应该就属淡入和淡出了,这时候,我们应该只有一种选择:opacity配合animation,因为visibility这个属性是无法进行动画过渡的,要满足动画过渡,必须在两个值之间存在连续不断的值,即连续区间,visibility显然不满足,因为在可见/不可见两个状态之间不存在中间态,它是“布尔隐藏”的。

第二轮:子元素的表现

设置了opacity: 0和visibility: hidden 的元素,它们的子元素会受到怎样的不同影响呢?

首先,opacity属性是不可以被子元素继承的,而visibility属性可以被继承,详见CSS3规范opacityvisibility中的属性介绍。

其次,一旦父级元素设置了opacity,那么子元素的最大透明度将无法超过父级,意味着,父级的opacity为0.5,那么子级的opacity就算设置为1,其实际透明度也会是0.5 * 1 = 0.5,所以,只要父级透明度为0,那么子级没有任何办法可以重新设置为可见;

但visibility的子级却仍有“翻身”的机会,即使父级元素设置了visibility: hidden,子元素仍可通过visibility: visible重新设置为可见。

第三轮:层叠上下文(Stacking Context)

HTML中的元素都有自身的层叠水平,但是某些情况下,元素会形成层叠上下文(接下来用SC代替),直接“拔高”自身以及子元素的层叠水平。而元素间不同的层叠水平,在它们发生重叠的时候,就会决定谁将在Z轴上更高一筹,也就是谁离用户的眼睛更近。

至于什么情况下元素会形成SC,可以参考MDN文档的详细说明。而在这份文档中我们可以看到:当元素的opacity属性值小于1时,会形成SC。我们可以观察如下代码

<div style="position: relative;">
    <div style="position: absolute;background: green;
                top: 0;width: 200px;height: 200px">
    </div>
    <div style="background: red;width: 100px;height: 100px"></div>
</div>

这种情况下,设置了绝对定位的绿色方块形成了SC,所以其层叠水平自然比红色方块高,所以此时我们看不到红色方块。

而当我们为红色方块设置了opacity属性后,比如:

<div style="position: relative;">
    <div style="position: absolute;background: green;
                top: 0;width: 200px;height: 200px">
    </div>
    <div style="opacity: 0.5;background: red;width: 100px;height: 100px">
    </div>
</div>

此时,红色方块会层叠在绿色方块之上。因为红色方块的opacity小于1,形成了SC,且两者都未设置z-index,属于相同层叠水平,所以按照后来居上的原则,红色方块就会叠在上方。同理,opacity为0的元素也会创建SC,而visibility属性则不会创建SC,也不会影响到元素的层叠水平。

说了半天,有人可能会问,既然元素都隐藏了,看不见了,谁还管它在上在下呢?通常情况下是如此,但经过第四轮的PK后,你就会知道,有时候你的确不能忽视这个问题。


第四轮:可交互性/可访问性

这一轮我们比较的是可交互性/可访问性,先说visibility: hidden,设置了这个属性的元素,其绑定的监听事件将会忽略event.target为自身的事件触发。这句话比较拗口,通俗点说就是,这个元素会接收到子元素的事件冒泡,但无法触发自身的事件,可以通过这个在线demo体验一下这个效果。

当然,除了无法触发自身的事件之外,它还无法通过tab键访问到,也就是无法focus;此外,它还会失去accessibility,也就是不能进行无障碍访问,比如屏幕阅读软件将无法访问到这个元素。

反观设置了opacity: 0的元素,则完全没有以上的限制。现在你知道我们为啥不能忽视上一轮提出的问题了,因为设置了opacity: 0的元素即使看不见了,它仍然可以被点击被访问,有时会产生意料之外的bug。


取长补短

既然两者都有各自的优缺点,我们能否将其结合,并取长补短呢?

答案是当然可以。但首先要明确我们想取什么长,补什么短。一般来讲,我们既希望元素可以使用淡入淡出的动画效果,又希望在消失后不要保留可交互性/可访问性,其实做法很简单:

.box {
  animation: fade 0.5s linear 0s forwards;
}
@keyframes fade {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}

我们仍然使用opacity来做动画过渡,但在最后一个动画帧,我们把visibility: hidden加上,就可以达到我们想要的效果了。此时,当元素淡出后,也不会意外地触发事件了。并且,在使用opacity属性进行动画效果时,浏览器还会将该元素提升为composite layer(合成层),使用gpu进行硬件加速渲染,两全其美~

当然,如果你的确需要这个元素保留页面中的占位,就不能这样做了。


总结

总而言之,如果你没有动画需求,使用visibility进行显隐切换可能更省心,但如果有动画需求,则最好使用两者结合的方式。另外,以后会有更多的寻根问底系列的文章,目的就是要对小的知识点也进行深入剖析,从而获得更加系统性的认识,而不是停留在表面。


链接: https://fly63.com/article/detial/5148

使用原生js来控制、修改CSS伪元素的方法总汇, 例如:before和:after

在网页中,如果需要使用辅助性/装饰性的内容的时候,这就需要使用伪元素了。在使用伪元素的时候,会发现js并不真能直接控制它,这篇文章主要就介绍下如果间接的控制、修改css中伪元素的方法

CSS隐藏元素的五种方法

用css隐藏页面元素有许多种方法。1、opacity:0;2、visibility:hidden;3、diaplay:none;4、position:absolute;5、clip-path。大家可以根据具体情况选择适合的方法来隐藏元素

CSS隐藏页面元素常用方法_不同场景下使用CSS隐藏元素

使用 CSS 让元素不可见的方法很多,剪裁、定位到屏幕外、明度变化等都是可以的。虽然它们都是肉眼不可见,但背后却在多个维度上都有差别

js动态生成html元素并为元素追加属性

动态生成HTML元素的方法有三种:document.createElement()创建元素,再用appendChild( )添加、使用innerHTML直接将元素添加到指定节点、jQuery创建节点...

原生js删除元素

通过id删除;通过class获取元素;清空一个元素,即删除一个元素的所有子元素 ;原理很简单,就是不断的判断要清空的div还有没有子节点,有的话就删除一个子节点(这里是它的首个子节点),直到删除完毕为止。

原生JS如何获取当前元素属于父元素第几个子元素

我们经常通过document.getElementById 方法来获取到一个元素,这个时候我们经常需要有一个需求,那就是如何判断这个元素在父元素中的位置。原生JS有一个常见的小技巧那就是通过元素的previousSibling 属性,额外需要注意的是该属性会遍历text节点,即回车键。

在js中获取页面元素的属性值时,弱类型导致的诡异事件踩坑记录

前几天写一个js的时候遇到一个非常诡异的事情,这个问题是这样的,我要获取一个页面的DOM元素的val值,判断这个值是否比某个变量大,这个需求原先数字最大也就是10,现在要改了,可能会更多,这个时候我发现比较大小的判断就出了问题:

JS 创建元素的三种方法

动态创建元素一 document.write()body标签中就会插入但是这种方法几乎不用,因为这回影响页面的布局,甚至会将页面原来的内容冲刷掉,从而只显示输出内容;动态创建元素二 innerHTML

去除inline-block元素间的间距

真正意义上的inline-block水平呈现的元素间,换行显示或者空格隔开的情况下会有间距,这是因为浏览器在解析时,会将换行等读取成一个空格导致。

什么是可替换元素?

请问什么是可替换元素和非可替换元素,它们的差异是什么?并举例说明。前端面试中 HTML 的题目本来就最少,而且并不难,翻来覆去也就那几样。我们之前已经谈到过最经典的 HTML 语义化 ,今天就借此机会来谈谈可替换元素。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!