stacking context 会导致 position: fixed 失效,或者出现一些异常表现。(不同的浏览器内核有不同的表现)
转载自 掘金
文档中的层叠上下文由满足以下任意一个条件的元素形成:
- 根元素 (HTML),
- z-index 值不为 "auto"的 绝对/相对定位,
- 一个 z-index 值不为 "auto"的 flex 项目 (flex item),即:父元素 display: flex|inline-flex,
- opacity 属性值小于 1 的元素(参考 the specification for opacity),
- transform 属性值不为 "none"的元素,
- mix-blend-mode 属性值不为 "normal"的元素,
- filter值不为“none”的元素,
- perspective值不为“none”的元素,
- isolation 属性被设置为 "isolate"的元素,
- position: fixed
- 在 will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值(参考 这篇文章)
- -webkit-overflow-scrolling 属性被设置 "touch"的元素
为什么突然就写这个东西呢?
原因是:昨天,我写的一个Vue项目在iOS里变现有些异常,主要是我对一个背景图进行了绝对定位(position:fiexd
),但是页面滚动的时候,这个背景图也跟着页面抖动或者跟着页面滚动,页面滚动停止后,背景图又恢复到了原样。虽然以前也遇到过,然后一顿瞎操作最后就解决了,但是没有真正的了解造成这个bug的本质。
根据上面对display: fixed失效原因的描述,突然想起来前几天我在项目里新增了一个 -webkit-overflow-scrolling: touch;
这个css属性。
解决办法
-
删除```-webkit-overflow-scrolling: touch;``` 靠document滚动
-
fixed定位的元素要放在 设置```-webkit-overflow-scrolling: touch;```元素的同级,免受影响。
纠结了好久,也查了好多资料,最终选择了第一个方法,干脆把-webkit-overflow-scrolling: touch;
删除,然后fixed定位的元素也不受干扰了。
另一个问题就是,这是个vue项目,我本来可以通过给body、#app、#app>div
设置了高度100%,然后在组件内部可以直接写 @scroll="scrollPage"
监听当前组件内部的滚动事件。现在由于把-webkit-overflow-scrolling: touch;
删除了,相应的我还把body、#app、#app>div
高度设置设置成了自动。所以没法监听组件内部元素的滚动。
最后只能在进入组件的时候,给window添加scroll监听事件。然后在销毁钩子中移除监听事件。
先贴出来:
scrollPage() {
if (this.isEnd) {
return;
}
const supportPageOffset = window.pageXOffset !== undefined;
const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
// eslint-disable-next-line no-nested-ternary
const scrollTop = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
const winH = window.innerHeight;
const allH = this.$refs.couponList.offsetHeight;
if (allH - winH - scrollTop < 50) {
this.start = this.couponList.length;
this.getList();
}
},
在获取页面向上滚动了多少px的时候,为了跨浏览器兼容,请使用 window.pageYOffset 代替 window.scrollY。另外,旧版本IE(<9)两个属性都不支持,必须使用其他的非标准属性。完整的兼容性代码如下: 下面这段来自MDN
const supportPageOffset = window.pageXOffset !== undefined;
const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
// eslint-disable-next-line no-nested-ternary
const scrollTop = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;