相信小伙伴们都被问过这样一个问题:让一个元素隐藏起来,有多少种方法呢?常规来说,咱们有三种方法display: none
、opacity: 0
和visibility: hidden
,基于display: none
的反作用,已是个被说烂的问题,主要是有如下缺点:css
1、切换显隐时会致使reflow(回流),从而引发repaint(重绘),当页面中reflow增多至必定程度时,会致使cpu使用率飙高。html
2、没法对元素设置过渡动画,也没法进行方位测量(包括clientWidth, clientHeight, offsetWidth, offsetHeight, scrollWidth, scrollHeight, getBoundingClientRect(), getComputedStyle())前端
缘由是:浏览器会解析HTML标签生成DOM Tree,解析CSS生成CSSOM,而后将DOM Tree和CSSOM合并生成Render Tree,最后才根据Render Tree的信息布局来渲染界面,但设置了display: none
的元素,是不会被加入Render Tree中的,天然也没法渲染过渡动画。浏览器
3、用它来设置显隐切换时,会由于与display: flex
、display: grid
冲突而令人困扰。微信
如此说来,咱们设置元素显隐时,使用opacity或visibility彷佛是更好的选择,但小伙伴们有没有考虑过,opacity: 0
和visibility: hidden
这二者又有何具体区别呢?
既然标题写着寻根问底,那么咱们就经过几轮PK来深挖一下这二者的具体区别:ide
常见的动画效果中,使用最普遍的应该就属淡入和淡出了,这时候,咱们应该只有一种选择:opacity配合animation,由于visibility这个属性是没法进行动画过渡的,要知足动画过渡,必须在两个值之间存在接二连三的值,即连续区间,visibility显然不知足,由于在可见/不可见两个状态之间不存在中间态,它是“布尔隐藏”的。布局
设置了opacity: 0
和visibility: hidden
的元素,它们的子元素会受到怎样的不一样影响呢?flex
首先,opacity属性是不能够被子元素继承的,而visibility属性能够被继承,详见CSS3规范opacity和visibility中的属性介绍。动画
其次,一旦父级元素设置了opacity,那么子元素的最大透明度将没法超过父级,意味着,父级的opacity为0.5,那么子级的opacity就算设置为1,其实际透明度也会是0.5 * 1 = 0.5,因此,只要父级透明度为0,那么子级没有任何办法能够从新设置为可见;ui
但visibility的子级却仍有“翻身”的机会,即便父级元素设置了visibility: hidden
,子元素仍可经过visibility: visible
从新设置为可见。
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进行显隐切换可能更省心,但若是有动画需求,则最好使用二者结合的方式。另外,之后会有更多的寻根问底系列的文章,目的就是要对小的知识点也进行深刻剖析,从而得到更加系统性的认识,而不是停留在表面。