解决滚动条的出现压缩元素宽度问题

这个问题是怎么发现的呢 ——今天用到了 ElementUI 的固定表头的表格这个组件,发现这个组件下方滚动的部分是不会随着滚动条消失而产生抖动的,这和我自己平时写的宽度 100%的元素表现不一样,因为滚动条生成之后会挤压元素的宽度,缩短一个滚动条的宽度。在用table标签实现的固定表头表格中会导致表头与列表内容宽度不一致。

其他会导致该问题出现的场景如张鑫旭博客1中提到:

  1. 信息流页面,如新浪微博,是从上往下 push 渲染的。开始只有头部一些信息加载,此时页面高度有限,没有滚动条;然后,更多内容显示,滚动条出现,占据可用宽度,margin: 0 auto 主体元素自然会做偏移——跳动产生。
  2. JS 交互,本来默认页面高度不足一屏,结果点击了个“加载更多”,内容超过一屏,滚动条出现,页面主体就会左侧跳动。
  3. 结构类似几个页面通过头部的水平导航刷新切换,结果有的页面有滚动条,有的没有。造成的结果就是,导航会跳来跳去!

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%);

因为100vwwindow的宽度,其实就是 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;
}

Footnotes

  1. 小 tip:CSS vw 让 overflow:auto 页面滚动条出现时不跳动 « 张鑫旭-鑫空间-鑫生活