CSS中用 opacity、visibility、display 属性将 元素隐藏 的 对比分析

版权声明:本文为博主原创文章,欢迎转载,转载请注明出处。 https://blog.csdn.net/FE_dev/article/details/80383232

说明

opacity 用来设置透明度
display 定义建立布局时元素生成的显示框类型
visibility 用来设置元素是否可见。
opacity、visibility、display 这三个属性分别取值 0、hidden、none 都能使元素在页面上看不见,但是他们在方方面面都还是有区别的。

是否占据页面空间

举个例子

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
    }
    .red{
        width:100px;
        height:100px;
        background:red;
    }
  </style>
 </head>
 <body>
    <div class="yellow"></div>
    <div class="red"></div>
 </body>
</html>

最开始的样子
这里写图片描述

黄色块div元素 使用 opacity:0;
这里写图片描述

黄色块div元素 使用 visibility:hidden;
这里写图片描述

黄色块div元素 使用 display:none;
这里写图片描述

可以看出,使用 opacity 和 visibility 属性时,元素还是会占据页面空间的,而使用 display 属性时,元素不占据页面空间。

对子元素的影响

如果子元素什么都不设置的话,都会受父元素的影响,和父元素的显示效果一样,我们就来举例看看,如果子元素设置的值 和 父元素设置的值不同会有什么效果。
例子 (opacity属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        opacity:0;  /* 父元素的 opacity属性取值为0 */
    }
    .blue{
        width:50px;
        height:50px;
        background:blue;
        opacity:1;   /* 子元素的 opacity属性取值为1 */
    }
  </style>
 </head>
 <body>
    <div class="yellow">
        <div class='blue'></div>
    </div>
 </body>
</html>

这里写图片描述

例子 (visibility属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        visibility:hidden;  /* 父元素的 visibility属性取值为hidden */
    }
    .blue{
        width:50px;
        height:50px;
        background:blue;
        visibility:visible;  /* 子元素的 visibility属性取值为visible */
    }
  </style>
 </head>
 <body>
    <div class="yellow">
        <div class='blue'></div>
    </div>
 </body>
</html>

这里写图片描述

例子 (display属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        display:none;  /* 父元素的 display属性取值为none */
    }
    .blue{
        width:50px;
        height:50px;
        background:blue;
        display:block;  /* 子元素的 display属性取值为block */
    }
  </style>
 </head>
 <body>
    <div class="yellow">
        <div class='blue'></div>
    </div>
 </body>
</html>

这里写图片描述

可以看出,使用 opacity 和 display 属性时,父元素对子元素的影响很明显,子元素设置的 opacity 和 display 属性是不起作用的,显示的效果和父元素一样,而使用 visibility 属性时,子元素如果设置为 visibility:visible; 并没有受父元素的影响,可以继续显示出来。

自身绑定的事件是否能继续触发

这里说的触发事件,是指用户人为的触发的事件,不包括使用 JavaScript 模拟触发的事件。
例子 (opacity属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        opacity:0;
    }

  </style>
 </head>
 <body>
    <div class="yellow" onmouseenter="alert(0)"></div>
 </body>
</html>

这里写图片描述

例子 (visibility属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        visibility:hidden;
    }

  </style>
 </head>
 <body>
    <div class="yellow" onmouseenter="alert(0)"></div>
 </body>
</html>

这里写图片描述

使用 display:none; 就不用举例子了,因为使用 display 属性的话,元素不仅看不见,而且也不占据页面空间,所有不会触发事件。

总的来说,使用 visibility 和 display 属性,自身的事件不会触发,而使用 opacity 属性,自身绑定的事件还是会触发的。

是否影响其他元素触发事件

例子(opacity属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
       .red{
         width:400px;
         height:40px;
         background:red;
         position:relative;
       }

       .yellow{
        position:absolute;     
        top:0;
        left:0;
        width:200px;
        height:300px;
        background:yellow;
        opacity:0;            
       }

       .blue{
         width:200px;
         height:200px;
         background:blue;
       }
       .red:hover .yellow{
         opacity:1;          
       }
  </style>
 </head>
 <body>
      <div  class='red'>
         <div class='yellow'></div>
      </div>

      <p  class='blue' onmouseenter=alert(0)></p>
 </body>
</html>

这里写图片描述
黄色块div元素设置 opacity:0;,通过定位,遮挡住了 蓝色的p元素,当鼠标移到蓝色p元素上时,并没有触发蓝色p元素的事件。

例子(visibility属性)

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
       .red{
         width:400px;
         height:40px;
         background:red;
         position:relative;
       }

       .yellow{
        position:absolute;     
        top:0;
        left:0;
        width:200px;
        height:300px;
        background:yellow;
        visibility:hidden;          
       }

       .blue{
         width:200px;
         height:200px;
         background:blue;
       }
       .red:hover .yellow{
         visibility:visible;       
       }
  </style>
 </head>
 <body>
      <div  class='red'>
         <div class='yellow'></div>
      </div>

      <p  class='blue' onmouseenter=alert(0)></p>
 </body>
</html>

这里写图片描述
黄色块div元素设置 visibility:hidden;,通过定位,虽然遮挡住了 蓝色的p元素,但是当鼠标移到蓝色p元素上时,还是触发了蓝色p元素绑定的事件。

和上边一样,display 属性就不举例子了,因为他不会占据页面空间,也就不会遮挡其他元素,就不会影响其他元素触发事件了。
所以,visibility 和 display 属性是不会影响其他元素触发事件的,而 opacity 属性 如果遮挡住其他元素,其他的元素就不会触发事件了。

是否产生回流(reflow)

回流

当页面中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(也有人会把回流叫做是重布局或者重排)。
每个页面至少需要一次回流,就是在页面第一次加载的时候。

dispaly 属性会产生回流,而 opacity 和 visibility 属性不会产生回流。

是否产生重绘(repaint)

重绘

当页面中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的时候,比如background-color。则称为重绘。

dispaly 和 visibility 属性会产生重绘,而 opacity 属性不一定会产生重绘。

元素提升为合成层后,transform 和 opacity 不会触发 repaint,如果不是合成层,则其依然会触发 repaint。
在 Blink 和 WebKit 内核的浏览器中,对于应用了 transition 或者 animation 的 opacity 元素,浏览器会将渲染层提升为合成层。
也可以使用 translateZ(0) 或者 translate3d(0,0,0) 来人为地强制性地创建一个合成层。

举个例子

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
</head>
<body>
    <div id="target">重绘 repaint</div>

    <script>
        var flag = false;
        setInterval(function () {
            flag = !flag;
            target.style.opacity = flag ? 0 : 1;
        },1000)
    </script>
</body>
</html>

我们可以用 Chrome DevTools 的 Rendering 来看看,
先打开 Rendering
这里写图片描述
把第一个选项勾选,这个选项会 高亮显示需要重绘的区域。
这里写图片描述
看看效果
这里写图片描述

改改代码,增加上个 transition

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <style>
    div{
        transition:1s;
    }
  </style>
</head>
<body>
    <div id="target">重绘 repaint</div>

    <script>
        var flag = false;
        setInterval(function () {
            flag = !flag;
            target.style.opacity = flag ? 0 : 1;
        },1000)
    </script>
</body>
</html>

再看看效果
这里写图片描述

加上 transition 后就没有 高亮显示了,这时候 opacity 不会触发重绘。

实际上透明度改变后,GPU 在绘画时只是简单的降低之前已经画好的纹理的 alpha 值来达到效果,并不需要整体的重绘。不过这个前提是这个被修改的 opacity 本身必须是一个图层,如果图层下还有其他节点,GPU 也会将他们透明化。

注意:回流必将引起重绘,而重绘不一定会引起回流。

是否支持transition

opacity 是支持 transition的,一般淡入淡出的效果就是这样实现的。
这里写代码片

visibility 也是支持 transition 的。

visibility: 离散步骤,在0到1数字范围之内,0表示“隐藏”,1表示完全“显示”

visibility : hidden; 可以看成 visibility : 0;
visibility : visible; 可以看成 visibility : 1;

只要 visibility 的值大于0就是显示的,所以
visibility:visible 过渡到 visibility:hidden,看上去不是平滑的过渡,而是进行了一个延时。

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
  .blue{
    width:200px;
    height:200px;
    background:blue;
    transition:1s;
    visibility:visible;
  }
  .blue:hover{
    visibility:hidden;
  }
  </style>
 </head>
 <body>
    <div class='blue'></div>
 </body>
</html>

这里写图片描述
而如果 visibility:hidden 过渡到 visibility:visible ,则是立即显示,没有延时。
注意
上面这个例子只能是从 visibility:visible 过渡到 visibility:hidden,不能从 visibility:hidden 过渡到 visibility:visible
当元素是 visibility:hidden; 时,自身的事件不会触发,所以像上面这个例子中,直接在蓝色块div元素 上加 hover 事件,要去将自身的 visibility:hidden 过渡到 visibility:visible 是不会起作用的。
但是在其他元素上加事件,来将该元素的 visibility:hidden 过渡到 visibility:visible 是可以的,看例子。

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
  .red{
    width:200px;
    height:200px;
    background:red;
  }
  .blue{
    width:200px;
    height:200px;
    background:blue;
    transition:1s;
    visibility:hidden;
  }
  .red:hover+.blue{
    visibility:visible;
  }
  </style>
 </head>
 <body>
        <div class='red'></div>
        <div class='blue'></div>
 </body>
</html>

这里写图片描述

display 不仅不支持transition,它还会使 transition 失效。举个例子看看

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .blue{
        width:200px;
        height:200px;
        background:blue;
    }
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        opacity:0;
        display:none;
        transition:1s
    }
    .blue:hover .yellow{
        opacity:1;
        display:block;
    }
  </style>
 </head>
 <body>
    <div class='blue'>
        <div class='yellow'></div>
    </div>
 </body>
</html>

这里写图片描述

可以看出用了display,支持 transition 的 opacity 属性也没起作用。

这是因为display:none; 的元素,是不会渲染在页面上的,而 transition 要起作用,元素必须是已经渲染在页面上的元素,我们可以再来看个例子

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .blue{
        width:200px;
        height:200px;
        background:blue;
    }
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        opacity:0;
        transition:1s
    }
  </style>
 </head>
 <body>
    <span>渲染到页面</span>
    <div class='blue'></div>

    <script>    
        var span = document.querySelector('span');
        span.addEventListener('click',function(){
            var yellowDiv = document.createElement('div');
            yellowDiv.classList.add('yellow');

            var blue = document.querySelector('.blue');
            blue.appendChild(yellowDiv);

            yellowDiv.style.opacity = '1';
        })
    </script>
 </body>
</html>

这里写图片描述
给 span 元素绑定事件,点击它的时候,才会把黄色块div元素,渲染到DOM树上,然后改变黄色块div元素的 opacity 属性,opacity 是支持 transition 的,而在这段代码中,并没有起作用。
更详细的关于 transition 是否成功 的解读看这里
渲染树决定 transtion 能否成功

要想解决这个问题,我们可以这样做。
1、把 display 属性换成 visibility 属性

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .blue{
        width:200px;
        height:200px;
        background:blue;
    }
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        opacity:0;
        visibility:hidden;
        transition:1s
    }
    .blue:hover .yellow{
        opacity:1;
        visibility:visible;
    }

  </style>
 </head>
 <body>
    <div class='blue'>
        <div class='yellow'></div>
    </div>
 </body>
</html>

这里写图片描述

2、如果必须要用 display 属性,我们可以加上定时器来解决这个问题

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <style>
    .blue{
        width:200px;
        height:200px;
        background:blue;
    }
    .yellow{
        width:100px;
        height:100px;
        background:yellow;
        display:none;
        opacity:0;
        transition:1s;
    }
  </style>
 </head>
 <body>
    <div class='blue'>
        <div class='yellow'></div>
    </div>
    <script>    
        var blue = document.querySelector('.blue');
        blue.addEventListener('mouseenter',function(){
            var yellowDiv = document.querySelector('.yellow');
            yellowDiv.style.display = 'block';
            setTimeout(function(){
                yellowDiv.style.opacity = '1';
            },0);
        })
    </script>
 </body>
</html>

这里写图片描述

总结

opacity : 0 visibility : hidden display : none
是否占据页面空间 占据 占据 不占据
子元素设置该属性其他值是否可以继续显示 不可以 可以 不可以
自身绑定的事件是否能继续触发 能触发 不能触发 不能触发
是否影响遮挡住的元素触发事件 影响 不影响 不影响
属性值改变是否产生回流(reflow) 不产生 不产生 产生
属性值改变是否产生重绘(repaint) 不一定产生 产生 产生
该属性是否支持transition 支持 支持 不支持

这里写图片描述

猜你喜欢

转载自blog.csdn.net/FE_dev/article/details/80383232