前几天在帮一个同事看了个问题,就是他的老项目里(用的jquery)写的,用iScroll插件做滚动加载的时候,在他的iphone7手机上,指定区域只能移动6px左右,这明显不正常,其他同事包括我的(8plus)是正常的,然后帮他找原因, 首先查看了下iScroll的版本4.25,好像也没问题啊。
一、 问题分析
1、onBeforeScrollStart方法:
onBeforeScrollStart: function(e) {
e.preventDefault();
}
此方法是在_start里进行了调用,目的是为了阻止浏览器默认动作的执行,防止在滑动的过程中进行干扰,同时也就阻止了滑动区域里元素的事件的触发,这种处理方式也直接导致了必须要在_end方法中再次触发元素的点击事件。
2、_end方法
if (target.tagName != 'SELECT' && target.tagName != 'INPUT'
&& target.tagName != 'TEXTAREA') {
ev = document.createEvent('MouseEvents');
ev.initMouseEvent('click', true, true,
e.view, 1,point.screenX,
point.screenY, point.clientX,
point.clientY,e.ctrlKey,
e.altKey, e.shiftKey,
e.metaKey,0, null);
ev._fake = true;
target.dispatchEvent(ev);
}
这个处理方式就是顺承了上面所提到的阻止了浏览器默认行为后,对滑动区域除select、input、textarea外的元素触发click事件,已完成对click绑定事件的调用。
二、 具体分析
由于onBeforeScrollStart是在_start方法中进行的调用,e.preventDefault();阻止了元素的默认行为,从而导致了元素绑定事件的失效,必须在_end操作结束后进行绑定事件的模拟调用,原始的iScroll源代码中在_end中最后创建了click事件的模拟,但是这里必须要清楚的一个原理就是,click其实是要依赖于其他事件的:
-
1 普通pc网页中,click需要依赖于mousedown、mouseup的相继触发
-
2 移动webkit中,click则需要依赖于touchstart、touchend(实际mousedown,mouseup在移动webkit上也存在)的相继触发 “相继触发”的意思就是中间不会夹杂有其他的事件类型,这也就很容易理解iScroll中在_end中对模拟事件调用的条件了,必须要判断that.moved才能直接触发模拟事件。
iScroll中与_start、_move、_end相关的三个事件类型是按照如下的规则来设置的:
START_EV = hasTouch ? 'touchstart' : 'mousedown'
MOVE_EV = hasTouch ? 'touchmove' : 'mousemove'
END_EV = hasTouch ? 'touchend' : 'mouseup'
三、 具体说明
经测试,部分手机里默认浏览器里会默认所有元素都有一个默认的click事件(测试结果显示鼠标事件中默认事件包含mouseup,mousedown,click,dblclick在移动webkit上不支持,为系统放大功能),e.preventDefault时会阻止掉默认click事件的执行,必须要人为的在_end结束之后模拟click事件的调用,而其他正常手机即使调用了e.preventDefault也不会阻止click事件的触发,因为这些手机的默认浏览器上的元素的click事件的cancelable属性不为true,不可以被preventDefault取消掉,会正常执行,而如果在_end中模拟了click事件则将会导致click的重复调用(在有toggle状态的事件上非常明显),因此折中的方式参见下面的解决方案。
四、 解决方案
1. 去除onBeforeScrollStart里的阻止默认行为:
onBeforeScrollStart: function (e) {
//e.preventDefault();
}
2.onBeforeScrollMove设置为:
onBeforeScrollMove:function (e) {
e.preventDefault();
}
以保证手机上的正常滑动免受浏览器默认行为影响(如下滑时会有窗口的scroll事件),当然如果这里不添加的话也可以在document的END_EV中阻止浏览器默认行为
3. _end 中将模拟事件名更改为END_EV或者直接去掉模拟事件的功能
iscroll.js是Matteo Spinelli开发的一个js文件,使用原生js编写,不依赖与任何js框架。旨在解决移动webkit系浏览器的区域滚动问题,兼容mobile、safari、android默认浏览器、safari、chrome、firefox5+、opera11+、IE9+及其他webkit核心浏览器。
5、最后说明
最后,我在此次解决滚动失效的过程中,针对该页面做了一个最简单的处理如下:
// 获取所需要滚动区域的元素
var scrollPage = document.querySelector('.scrollPage');
// 取消事件默认动作
scrollPage.addEventListener('touchmove',function(e){
e.preventDefault();
});
这样页面即可恢复滚动效果了。