-webkit-overflow-scrolling:touch
前言
最近用ionic
来开发微信公众号,看好的就是ionic
有成熟的UI框架,不需要自己去定义UI控件,当然微信也提供了自家的UI控件,但是数量实在太少,不敢恭维。
因为一直用的是chrome
调试开发的页面,测试的时候也没有发现什么问题,但是直到将代码放上服务器,然后通过iPhone
手机的微信公众号来访问开发的网页,在滑动到顶部或者底部的时候,重复上滑或者重复下滑,会导致页面卡死,需要2,3秒后才能恢复正常。瞬间奔溃,这是个什么玩意儿。在百度上查了一下资料原来是-webkit-overflow-scrolling:touch
引起的,有前端开发经验的人都知道,这个属性是给网页在iOS
设备中呈现并滚动的时候,起到更加流畅的作用,有一个回弹的效果,跟原生iOS
滑动的效果一样,没想到反而给iOS
设备挖了这么大的一个坑,wtf....
分析
经网上查找一些资料描述是Safari
会对使用-webkit-overflow-scrolling
的网页,会创建一个UIScrollView
,给要显示的元素使用。具体可以参考这篇文章,文章的作者也是遇到这个问题,并且估计快被逼疯了...
通过chrome
检查元素可以看到在网页编译并且跑起来之后,ionic
有生成了一个类名为“scroll-content”
的div
,这个div
就是使用了-webkit-overflow-scrolling:touch
,仔细查看了资料包括上面那篇文章,说是可以通过给滚动的元素的内部加一个div元素,然后设置这个内部的div的高度100%+1px
或100%+1%
可以解决,有些人就说是通过修改z-index
,还有些人说是不要让那个滚动的元素有relative
或者absolute
这样的css定位。由于是在ionic
的框架上进行开发的页面,查找了元素“scroll-content”
这个div确实有绝对定位,刚开始很担心是ionic
框架的问题,难道这次要翔,但是上面的方法全部使了个遍,可以很负责任的告诉你,没有用,没有用,没有用,不论是ios
设备的微信浏览器,safari
浏览器,QQ
浏览器,UC
浏览器,都是会存在这个卡死的问题。
解决方案
想了好久,既然是-webkit-overflow-scrolling:touch
引起的,那么不用这个属性就行了嘛,但是不用这个属性,页面滑动起来真的是还不如让这个bug
留着,那感觉就像你看了一部无声的,黑白的,画质贼差的电影。接着往下思考,既然是在顶部或者底部的时候会出现卡死,那么不如通过touchstart
和touchmove
事件来判断是否到达顶部或者底部,然后移除-webkit-overflow-scrolling:touch
,不满足的时候就重新加上-webkit-overflow-scrolling:touch
,这样就不会影响滑动,实际上也是有点效果了,可以滑动一点点,但是你疯狂的滑动,还是会出现卡死。
当然这种方案是不行的,所以最后还是通过判断是否到达底部或者顶部添加event.preventDefault();
来解决,在网上也有人
说这样是可行的。
但是这边还有一个需要注意的点就是,如果你的页面有刷新和加载分页,那么你就需要注意设置event.preventDefault()
的时机。
如果有下拉刷新,那么在scrollTop为0的时候,就不能设置event.preventDefault();如果有加载分页,那么你就需要直到滑动到最后一页的时候,才能设置event.preventDefault(),否者你一加载更多,页面立马就卡死了。
下面是我代码:
preventFreezeAtTopOrBottomForIOS(isGetRefresher,contenClassName){
if(this.isIOS()){
let contentEle = document.getElementsByClassName(contenClassName)[0];
let lastY = 0; // Needed in order to determine direction of scroll.
contentEle.getElementsByClassName("scroll-content")[0].addEventListener('touchstart', function(event) {
lastY = event.touches[0].clientY;
});
//获取滚动元素
let scrollEle = contentEle.getElementsByClassName("scroll-content")[0];
//先移除监听事件
scrollEle.removeEventListener('touchmove', function (event) {
event.preventDefault();
}, false);
//再添加监听事件
scrollEle.addEventListener('touchmove', function(event) {
let top = event.touches[0].clientY;
// Determine scroll position and direction.
let scrollTop = scrollEle.scrollTop;
// console.log("--scrollTop--" + scrollTop);
let scrollHeight = scrollEle.scrollHeight;
// console.log("--scrollHeight--" + scrollHeight);
let clientHeight = scrollEle.clientHeight;
// console.log("--clientHeight--" + clientHeight);
let direction = (lastY - top) < 0 ? "up" : "down";
// FIX IT!
if (scrollTop == 0 && direction == "up") {
console.log("--到顶部--");
// Prevent scrolling up when already at top as this introduces a freeze.
if(!isGetRefresher){//没有头部刷新的,需要设置防止头部freeze
event.preventDefault();
}
} else if (scrollTop >= (scrollHeight - clientHeight) && direction == "down") {
// Prevent scrolling down when already at bottom as this also introduces a freeze.
event.preventDefault();
console.log("--到底部--");
}
lastY = top;
});
}
}
由于我是使用ionic
框架来开发的,所以我这边实际上滚动是“scroll-content”
这个div,并且每一个页面都有一个“scroll-content”
,所以你在获取对应的页面对应不同的“scroll-content”
的时候,可以通过给ion-content
设置class
来获取其下面的元素“scroll-content”
。
参数说明:
其中的contentClassName
就是各个页面的ion-content
对应的classname
,isGetRefresher
就是判断是否有下拉刷新,是boolean
类型,至于加载更多,你不需要限制,只需要我上面说的,加载到最后再调用上面那段代码。反正就是内容高度一变化,你就得重新设置。
总结
感觉这种方法是比较好的解决方法了。如果你有其他更好的方法可以告诉我(估计你也没有😄),被这个bug
折磨了很久,总算是消停了,当然还是希望官方可以解决这个bug
。