这个问题是怎么发现的呢 ——今天用到了 ElementUI 的固定表头的表格这个组件,发现这个组件下方滚动的部分是不会随着滚动条消失而产生抖动的,这和我自己平时写的宽度 100%的元素表现不一样,因为滚动条生成之后会挤压元素的宽度,缩短一个滚动条的宽度。在用table
标签实现的固定表头表格中会导致表头与列表内容宽度不一致。
其他会导致该问题出现的场景如张鑫旭博客1中提到:
- 信息流页面,如新浪微博,是从上往下 push 渲染的。开始只有头部一些信息加载,此时页面高度有限,没有滚动条;然后,更多内容显示,滚动条出现,占据可用宽度,margin: 0 auto 主体元素自然会做偏移——跳动产生。
- JS 交互,本来默认页面高度不足一屏,结果点击了个“加载更多”,内容超过一屏,滚动条出现,页面主体就会左侧跳动。
- 结构类似几个页面通过头部的水平导航刷新切换,结果有的页面有滚动条,有的没有。造成的结果就是,导航会跳来跳去!
Element 的实现应该是考虑过了这个问题,将表格的容器宽度和高度都预留出了一个滚动条宽度的 gutter,然后通过resize()
函数去算出元素的宽度并以像素值的形式写进宽度的样式里。所以问题大概可以归结为以下信息。
问题描述
滚动条要占用一定的宽度,每个浏览器滚动条宽度不尽相同。比如 Chrome 的为 17px
在页面突然变长时,会出现滚动条,滚动条的出现挤压了页面,使得页面出现“抖动”的现象。
问题解决
滚动条常驻(几乎不会用到)
overflow-y: scroll;
overlay
overflow: overlay;
Chrome 太强大了,其他浏览器追不上它的脚步。因此 overlay 只支持 Chrome。Firefox 不认识这个属性值。
Chrome 下overflow
有个新的属性值overlay
,这个属性简直就是为了这个问题而生,他和auto
有点像,但是区别就是在触发滚动条时候并不挤压空间,说得直白点就像是移动端的悬浮滚动条,唯一的区别就是不会像手机上那样自动出现自动消失了,滚动条会遮盖住容器 17px 的空间。
利用 calc() 计算 margin-right
css3 提供了 calc()方法
margin-right: calc(100% - 100vw);
// 或
margin-left: calc(100vw - 100%);
// 或
padding-left: calc(100vw - 100%);
因为100vw
是window
的宽度,其实就是 window.innerWidth
, 而容器的宽度 100% 就是除了滚动条的可用宽度,因此在没有滚动条时候 calc(100% - 100vw)
就是 0,触发滚动条时候其值为负的滚动条宽度,我们将其赋值给容器的 margin-right
,即可巧妙补偿这个宽度的挤压,在滚动条存在的情况下容器宽度仍然占据整个视口的宽度。
终极方案
这个方案在张鑫旭博客上提到
经过一系列项目实践,关于浏览器出现滚动条和消失页面不滚动有了更加终极的解决方案,经过大型项目实践已经验证相当具有可行性
html {
overflow-y: scroll;
}
:root {
overflow-y: auto;
overflow-x: hidden;
}
:root body {
position: absolute;
}
body {
width: 100vw;
overflow: hidden;
}