面试官:请说一下什么是重绘和回流和visibility: hidden 和 display: none的区别
回流与重绘
概念
当JS对页面的节点进行操作时,就会产生回流或重绘回流/重排(reflow):
因为节点的尺寸、布局、显示(display: none/block)改变这些属性改变的时候,渲染树中的一部分或者全部需要重新构建,这种重新构建的现象就是回流。一个页面至少有一次回流。
重绘(repaint):
回流时,浏览器会看重新构建受影响部分的渲染树,只要渲染树一被改变或重新构建,就一定会引起重绘;回流完成后,浏览器根据新的渲染树重新绘制回流影响的部分节点,这个重新绘制的过程叫做重绘;
不改变节点原有的尺寸、布局、显示、增删,就只会引起重绘。
小结
回流一定会引起重绘,重绘不一定是回流产生的后续反应。因为只要不是改变物理的位置、尺寸、显示,就不会引起回流。
引起回流的因素:
DOM节点增加、删除
DOM节点位置变化
元素的尺寸、边距、填充(文字、图片)、边框、宽高
DOM节点display显示与否,不包含visibility
页面渲染初始化(第一次加载页面)
浏览器窗口尺寸变化(resize)
向浏览器请求某些样式信息(offset、scroll、client、width/height、getComputedStyle、currentStyle)
除开以上几个因素以外只会引起重绘。dom操作之所以消耗性能,就是因为容易引起回流,所以在做dom操作优化的时候就是以减少回流次数为依据来进行优化的,之所以需要缓存、文档碎片就是为了减少回流次数。
示例1
<div class="box">我是一个孤独的盒子</div> <script> // 专给一个节点增加样式就可以这样缓存 var oBoxStyle = document.getElementsByClassName('box')[0].style; //点语法是消耗性能的,这里缓存可以优化性能 // 回流 + 重绘 oBoxStyle.width = '200px'; // 回流 + 重绘 oBoxStyle.height = '200px'; // 回流 + 重绘 oBoxStyle.margin = '20px'; // 重绘 oBoxStyle.backgroundColor = 'green'; // 回流 + 重绘 oBoxStyle.border = '5px solid orange'; // 重绘 oBoxStyle.color = '#fff'; // 回流 + 重绘 oBoxStyle.fontSize = '30px'; </script>
示例2
<div class="box">我是一个孤独的盒子</div> <script> var h1 = document.createElement('h1'); h1.innerHTML = '我是一个孤独的标题'; document.body.appendChild(h1) </script>
h1追加在box后面只引起一次回流与重绘,但是如果追加在box前面的话,整个body里面的元素都会重新构建。所以要尽量避免插入在最前面。
在写dom的时候要考虑两个问题:
- 回流的问题;
- 回流所涉及到的节点数(一个父节点重新构建,里面的所有子节点都会重新构建)
display:none和visibility:hidden的区别是:
相同点:
两者都可以将dom元素隐藏
不同点:
1.display: none 隐藏之后不占用文档流(在渲染树里面不存在节点),而visibility: hidden却会占用文档流(在渲染树里面存在节点),如果要在隐藏元素的同时获取其尺寸信息,那就可以使用visibility: hidden
2.display: none 会引起页面的回流(重排)以及重绘,而visibility: hidden只会引起重绘,从性能角度上讲,visibility: hidden会稍微好点
3.display: none 的子元素不会进行显示,而visibility: hidden的子元素却是可以进行设置显示的
几种元素消失的属性:
- display: none; 元素消失,不占位;
- visibility: hidden; 元素消失,占位;
- opacity: 0; 透明度设为0,元素看不见,占位;
- width: 0; 宽度设为0,元素看不见,不占位。
1、display与元素的显隐
display: none显隐控制并不会影响CSS3 animation动画的实现,但会影响transition的过渡动画效果,因此如果要用到transition的话,建议使用visibility。
transition与display搭配使用时,会导致过渡消失,直接呈现或者消失元素,可能是transition过渡不支持display的改变,直接操作display会破坏transition的动画。
除了动画效果外,display: none还会影响CSS的计数队列。举个例子,10个列表从1开始计数,当第二个列表的display置为none,就会导致计数器忽略当前元素,原来的第三个列表则计数为2。
2、visibility与元素的显隐
visibility属性只是控制元素的可见性,不会使元素从渲染树中消失或改变整体布局,元素实际上已经存在于整个渲染树中,因此它的显隐不会触发浏览器的重排,仅仅是重绘。而display会让元素从渲染树中消失,是真正的不存在,因此如果显示的时候,会触发浏览器的重排,重新绘制整个渲染树。而重排是影响性能的主要问题,所以只要有可能,都要减少重排的出现。
visibility除了保留隐藏空间,还有很多有意思的属性,甚至跟display完全不一样的点。
首先visibility和display最大的区别点在于,父元素设置了visibility: hidden后,子元素也隐藏的深层原因在于子元素会继承父元素的visibility: hidden,因此,当我们需要隐藏父元素而又要显示部分子元素的时候,只需要把子元素继承的visibility改成默认的 visible属性即可,这点在被设置了display: none的元素上,无法实现。
visibility的元素的子元素只是单纯的继承了父元素的visibility,因此在CSS计数方面不会有任何影响,这点跟display: none也有所不同。同时transition过渡是支持visibility属性的,因此在使用过渡动画的时候,想让元素实现淡入淡出效果来控制显隐的可以使用visibility:hidden。