摘要
此bug出现需要条件:父元素需使用绝对定位absolute或固定定位fixed,使用overflow: scroll / auto(或overflow-y: scroll / auto),内部子元素是动态大小(例如较大的svg document,近似为内嵌iframe,等等)。
描述:
最近在项目中遇见的,比较奇异。最外层的div设置定位,里面的两个模块(main1、main2)设置了相对定位。并且这两个模块通过v-if判断,只能出现一个。main1里面有很多的其他元素组成,里面还分有多个小模块,且小模块都设置有高度。main2是动态生成的图片撑起来的高度,设置高度为100vh,在main2内只有定位设置一个信息提示。
结果:main1能正常滑动,main2在iOS部分(当时是ios10.x,ios11.x)系统机器不能滑动。
bug出现原因:没有相关官方文档描述该bug。在查阅文档及自己测试的时候总结:iOS safari 会将overflow:scroll的元素识别为一个单独的 ScrollView,并予以一个 -webkit-overflow-scrolling 属性为auto。而safari中的网页本身就是一个大的ScrollView,在脱离文档流的定位时,子元素的高度如果没有在ScrollView建立之前确定,就不会触发内部滑动,而会触发外部滑动。
关于 -webkit-overflow-scrolling:Safari CSS Reference官方是这样描述的:
Specifies whether to use native-style scrolling in an overflow:scroll element.
分析
1.父元素不脱离文档流时,无此bug。
2.父元素在不指定 -webkit-overflow-scrolling:touch时必定出现无法滑动的问题。
3.当内部元素为正常的html元素时,无此bug。
4.当为父元素重新设置overflow属性时,可能会导致safari重建ScrollView而bug消失。(之前版本的实验室用这种方法解决的,但新海外版不能用这种方法fix,所以是可能)
解决方法
1.必须为所有在移动端的overflow: scroll元素增加属性 -webkit-overflow-scrolling: touch。
2.当父元素可不脱离文档流时不要脱离文档流。
3.在子元素iframe加载完成后可异步将父元素的overflow: scroll属性重写(此方法可能不成功)。
4.如以上没有解决,则给予子元素一个min-height,大小不限(略大于效果最好),帮助safari建立ScrollView(亲测最有效)。
后续
之后浏览文章的时候发现了原因,记录下:
究其原因,是因为我在页面上放了很多张图片让其自行占位,而在页面刚加载时,其他浏览器会预先获取到图片的大小而给其一个占位,无论图片是否加载完成页面总高度固定的。而safari就不一样,图片没加载成功时高度是0。
图片没加载成功时高度是0!!!!
哦豁,这个要牢记,以后能多避坑
safari浏览器在渲染页面元素的时候,会预先走webkit浏览器的渲染流程:
1.构建DOM tree
2.构建CSS rule tree
3.根据DOM和CSS tree来构建render tree
4.根据render tree计算页面的layout
5.render页面
注意在第三步和第四步的时候,safari浏览器在构建render tree的时候,会预先找到相应的overflow: scroll元素,在计算页面layout的时候,会计算父元素的高度与子元素的高度,若子元素高于父元素,则在render页面时为其建立一个原生的scrollView。
这个scrollView有什么用的?其实就是为了给其一个弹弹乐的效果(但确实用户体验不错)。
当子元素是某个媒体格式时,比如img、object(svg)等,safari在加载完成之前是不会在计算在layout之内的,也就是高度为0,则子元素的高度就一定小于父元素的高度,safari不会给父元素一个原生的scrollView。