如何修复移动浏览器上 滑动后touchend不触发 事件不触发的bug

【移动端debug-3】部分安卓机型不触发touchend事件的解决方案 - 推酷
【移动端debug-3】部分安卓机型不触发touchend事件的解决方案
最近在项目中遇到一个奇怪的问题,有一个需求是这样:页面上有一个按钮,滚动页面时让它消失,停止滚动时让它显示。
常规思路:
step1、监听touchstart事件,记录Touch对象中pageY初始值startY;
step2、监听touchmove事件,记录Touch对象中pageY的变化后的值endY,当大于(endY-startY)的绝对值大于某个阈值时隐藏按钮;
step3、监听touchend事件,当触发touchend时,展现按钮
代码如下:
var startY,endY;
$("body").on("touchstart", touchStartRecord)
.on("touchmove", touchMoveHide)
.on("touchend", touchEndShow);
function touchStartRecord(event){
var touch = event.touches[0];
startY = touch.pageY;
function touchMoveHide(event){
var touch = event.touches[0];
endY = touch.pageY;
if (Math.abs(endY - startY) &= 5) {
//此处省略隐藏按钮的操作
function touchEndShow(event){
//此处省略重新展现按钮的操作
我们想得思路很清晰简洁,并且在iPhone上能顺利实现我们要的效果,但是尼玛到了安卓上,手指离开屏幕后,竟然按钮没有展现!!??WTF!
用工具调试,发现在触发touchend事件的函数里打断点,竟然进不去!!!
所以产生这一问题的原因找到了:touchend事件未被触发!
如何解决?
在stackoverflow上已经有相关话题的讨论,不少人提到,这个问题由来已久,已经给谷歌提bug了(谷歌传送门:
),但是最新的安卓版本还是没修复……再次WTF!!!
在讨论中有提到如下两种解决方案:
解决方案1:
在监听touchstart或者touchmove事件的函数里,阻止事件的默认行为event.preventDefault(),那么到touchend就能正常触发。
代码如下:
var startY,endY;
$("body").on("touchstart", touchStartRecord)
.on("touchmove", touchMoveHide)
.on("touchend", touchEndShow);
function touchStartRecord(event){
var touch = event.touches[0];
startY = touch.pageY;
function touchMoveHide(event){
var touch = event.touches[0];
endY = touch.pageY;
if (Math.abs(endY - startY) &= 5) {
//此处省略隐藏按钮的操作
event.preventDefault();
function touchEndShow(event){
//此处省略重新展现按钮的操作
尼玛,滚不动了啊……由于移动端touchmove事件的默认行为就是滚动页面,我们给阻止掉了,touchend是触发了,但是不是我们想要的效果。第三次WTF!!!
国外知名插件mobiscroll的博客里有分享关于这个问题的一些处理经验:(传送门:
On Android ICS if no
preventDefault
is called on&
touchstart
or the first&
events and the&
will not be fired. As a workaround we need to decide in the first&
if this is a scroll (so we don’t call&
preventDefault
) and then manually trigger&
touchend。
大意是:在安卓4.0系统(即Android ICS系统),如果在touchstart和第一个touchmove触发时,没有调用
preventDefault,那么后面touchmove(连续触发)以及最后的touchend都不会被触发。所以我们需要决定第一个touchmove是否是一个滚动事件(如果是,则不能preventDefault阻止默认行为)然后手动触发touchend。
解决方案2:
同时绑定touchcancel和touchend事件,这样在安卓上就能通过触发touchcancel来重新展示我们的按钮。
在touchcancel却能正常触发,而在我们的这个需求里,touchcancel的情况下,我们也是希望按钮重新展现的,那不正好就是我们想要的效果吗?
代码如下:
var startY,endY;
$("body").on("touchstart", touchStartRecord)
.on("touchmove", touchMoveHide)
.on("touchcancel touchend", touchEndShow);
function touchStartRecord(event){
var touch = event.touches[0];
startY = touch.pageY;
function touchMoveHide(event){
var touch = event.touches[0];
endY = touch.pageY;
if (Math.abs(endY - startY) &= 5) {
//此处省略隐藏按钮的操作
function touchEndShow(event){
//此处省略重新展现按钮的操作
好了,现在能够解决我们的需求了,但其实还不是最优解,因为我们如果还想给touchcancel单独增加一个操作,就不能够了。所以最根本的还是寄希望于谷歌尽早解决这个历史遗留bug。
已发表评论数()
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
没有分页内容
图片无法显示
视频无法显示
与原文不一致下次自动登录
现在的位置:
& 综合 & 正文
移动端WEB开发,click,touch,tap事件浅析
一、click 和 tap 比较
两者都会在点击时触发,但是在手机WEB端,click会有 200~300 ms,所以请用tap代替click作为点击事件。
singleTap和doubleTap 分别代表单次点击和双次点击。
二、关于tap的点透处理
在使用zepto框架的tap来移动设备浏览器内的点击事件,来规避click事件的延迟响应时,有可能出现点透的情况,即点击会触发非当前层的点击事件。
处理方式:
github上有一个叫做fastclick的库,它也能规避移动设备上click事件的延迟响应,
将它用script标签引入页面(该库支持AMD,于是你也可以按照AMD规范,用诸如require.js的模块加载器引入),并且在dom ready时初始化在body上,如:
$(function(){
newFastClick(document.body);
然后给需要“无延迟点击”的元素绑定click事件(注意不再是绑定zepto的tap事件)即可。
当然,你也可以不在body上初始化它,而在某个dom上初始化,这样,只有这个dom和它的子元素才能享受“无延迟”的点击
实践开发中发现,当元素绑定fastclick后,click响应速度比tap还要快一点点。哈哈
(2)、为元素绑定touchend事件,并在内部加上e.preventDefault();
$demo.on('touchend',function(e){
$demo.hide()
e.preventDefault();
三、touch事件touch是针对触屏手机上的触摸事件。现今大多数触屏手机webkit内核提供了touch事件的监听,让开发者可以获取用户触摸屏幕时的一些信息。
其中包括:touchstart,touchmove,touchend,touchcancel 这四个事件
touchstart,touchmove,touchend事件可以类比于mousedown,mouseover
,mouseup的触发。
touchstart : 当手指触摸到屏幕会触发;
touchmove : 当手指在屏幕上移动时,会触发;
touchend : 当手指离开屏幕时,会触发;
而touchcancel许多人不知道它在什么时候会被触发而忽略它,其实当你的手指还没有离开屏幕时,有系统级的操作发生时就会触发touchcancel,例如alert和confirm弹框,又或者是android系统的功能弹窗。
这4个事件的触发顺序为:
touchstart -& touchmove
-& …… -& touchmove -&touchend
但是单凭监听上面的单个事件,不足以满足我们去完成监听在触屏手机常见的一些手势操作,如双击、长按、左右滑动、缩放等手势操作。需要组合监听这些事件去封装对这类手势动作。
其实市面上很多框架都针对手机浏览器封装了这些手势,例如jqmobile、zepto、jqtouch,不过悲剧发生了,对于某些android系统(我自己测试到的在android 4.0.x),touchmove和touchend事件不能被很好的触发,举例子说明下:
比如手指在屏幕由上向下拖动页面时,理论上是会触发 一个 touchstart ,很多次 touchmove
,和最终的 touchend ,可是在android 4.0上,touchmove只被触发一次,触发时间和touchstart
差不多,而touchend直接没有被触发。这是一个非常严重的bug,在google Issue已有不少人提出
暂时我只发现在android 4.0会有这个bug,据说 ios 3.x的版本也会有。
而显然jqmobile、zepto等都没有意识到这个bug对监听实现带来的严重影响,所以在直接使用这些框架的event时,或多或少会出现兼容性问题!
&&&&推荐文章:
【上篇】【下篇】& & 对于前端开发者来说移动端存在更多的挑战,移动端页面开发过程中会碰到各种各样千奇百怪的问题(我们俗称BUG或坑),那么今天我为大家分享移动端页面开发过程中的一些坑和排坑技巧。
& & 移动端页面在不同设备、不同操作系统 、不同运行环境下都可能造成各种各样的没有碰到过的的坑,相比曾经的IE6坑多了。下面先介绍一下4类具体常见的坑:
& & A、页面高度渲染错误
& & 在各移动端浏览器中经常会出现这种页面高度100%的渲染错误,页面低端和系统自带的导航条重合了,高度的不正确我们需要重置修正它,通过javascript代码重置掉:
document.documentElement.style.height = window.innerHeight + 'px';
& & B、叠加区高亮
& & 在部分android机型中点击页面某一块区域可能会出现如图所示的黄色框秒闪,这是部分机型系统自身的默认定制样式,给该元素一个CSS样式重置掉:
-webkit-tap-highlight-color:rgba(0,0,0,0);
& & A、事件无法被触发
& & 在部分android机型的微信环境中会出现事件无法触发、表单无法输入的情况,我们针对需要输入或者触发事件的元素设置样式:-webkit-transform: translate3d(0,0,0) ,不过新版本的微信已经直接修复了该问题。
& & B、:active 效果不兼容
& & 在android 4.0版本以下CSS :active伪状态效果无法兼容,我们给该元素的touch系列的事件(touchstart/touchend/touchmove)绑定一个空匿名方法:
var element=document.getElementsById(&btnShare&);element.addEventListener(&touchstart&,function(){},false);
& & A、浏览器崩溃
var act = function(){ window.removeEventListener('devicemotion',act);};window.addEventListener('devicemotion',act,false);
& & 解绑函数写在了事件处理中导致小米手机中的微信崩溃,那么我们不要将解绑时间写在事件处理中即可。
& & B、预加载、自动播放无效
& & 如上表所示,经过简单的测试发现预加载、自动播放的有效性受操作系统、浏览器(webview)、版本等的影响,苹果官方规定必须由用户手动触发才会载入音频,那么我们捕捉一次用户输入后,让音频加载实现预加载:
//play and pause it oncedocument.addEventListener('touchstart', function () { document.getElementsByTagName('audio')[0].play(); document.getElementsByTagName('audio')[0].pause();});
& & C、无法同时播放多音频
& & 在android设备中,播放后一音频会打断前一音频,而不会同步播放,这个是目前系统资深决定的,我们只有采取优雅降权的方法让android选择不一样风格的音频前后切换播放而不是同时播放,达到与预期接近的音频效果。
& & D、不支持局部滚动
& & 在android 4.0版本以下在body(html)元素之外的元素 overflow:scroll 样式设置滚动条无效,这里有两种解决方案:
& & 1、巧用布局直接设置样式滚动条在body(html)上,其他元素&错觉滚动&。
& & 2、利用iscroll、自写js控制translate、scrollTop模拟
4、系统/硬件
& & A、怪异悬浮的表单
& & 在部分android 机型中的输入框可能会出现如图怪异的多余的浮出表单,经过观察与测试发现只有input:password类型的输入框存在,那么我们只要使用input:text类型的输入框并通过样式-webkit-text-security: 隐藏输入密码从而解决。
& & B、错误出现滚动条
& & 在游戏内嵌页中出现了不应该出现的滚动条,而且内容并没有超出内容区宽度,经过测试overflow:hidden 无效,通过一系列尝试使用古老的 &body scroll="no"& 写法解决,多尝试一下不同的写法和属性会有不一样的惊喜哦!
& & C、链接打开系统浏览器
& & 在游戏内webview的部分android机型中可能会出现点击链接调用系统浏览器的情况,这是一个非常不好的体验。那么我们尝试给这个元素添加 target="_blank"' 属性有可能解决,如果还不能解决那么需要修改IOS或android原生系统函数了。
& & D、Flex box 不兼容
& & 在游戏内嵌webview中碰到Flex box布局不兼容的情况,图中所示下面部分的导航错位了,虽然之前有仔细查看过Flex box的兼容性,但是在游戏内嵌页中无法确定其调用的系统浏览器版本及兼容,所以导致错误,所以我们写完整历史版本呢的3种Flex box 解决。那么我们思考在写页面过程中还是本着保守稳定的方式书写样式可以减少不不要的麻烦。
& & 面对这么多坑,还有各种各样已经的和未知的坑,时间上也不可能一一讲完,更不可能都已经有解决方案,我们需要学会如何去解决坑:
& & 解决坑首先自己需要有个强有力的执行力,通过不断去尝试来探索未知的问题,那么一般我们会通过哪些方式哪些步骤和技巧来尝试呢?
& & 1、首先我们需要定位问题,我们可以使用下列方法:
& & & & A、DOM隔离定位法
& & & & 不断由大范围到小范围隔离出DOM,从而找出、触发问题的DOM元素
& & & & B、样式、JS剔除法
& & & & 不断剔除一些JS、CSS观察调试检查是否是由部分JS、CSS属性引起问题
& & & & C、多运行环境测试法
& & & & 在多环境中测试,排查是否是由于特定环境引起
& & & & D、PC与手机联合调试法
& & & & 联合PC与手机进行定位,如:
& & 2、我们解决问题可以尝试如下的方式:
& & & * 尝试、转思维、绕解决
& & & * 优雅降权、区分处理、沟通
& & & * 搜索与提问......
& & &和以下的思维:
& & & * 替代
& & & * 绕道
& & & * 分割......
& & 3、在解决问题的过程中我们需要学会利用搜索和沟通资源解决问题:
& & & A、google
& & & B、stackoverflow
& & & C、同事、朋友、QQ群、论坛等
& & Google和stackoverflow让你更容易更精确的快速搜索出问题相关的资料,同时、朋友QQ群、论坛等让你可以直接的跟人沟通出别人所遇到的问题及解决方案。
& & 在发现、解决问题后,更重要的是要学会如何总结问题:
& & A、总结记录问题产生条件、解决方法和解决过程
& & B、尽可能分析原理、产生的条件,避免重蹈覆辙
& & C、分享给更多的人
& & 无穷无尽的坑,走的人多了,总结分享的多了,坑也就越来越平了
& &我们在总结记录问题的同时,整理了一个移动端小贴士,记录问题与一些坑,虽然目前还不完善但是希望能分享给更多的人也希望更多的人能参与完善。
& & 链接:&
分享到 &   
LAST WORKS
ideas第09期
Webpage Designed by TGodeas-腾讯游戏官方设计团队
Copyright & 2011. All Rights Reserved.最近有一些微信的项目,虽然页面很简单,但配合手势后的效果却是很不错的。最基本的效果就是手指向上滑,页面配合css3出现一个展开效果,手指向下滑将展开的内容按原路径收起。其实就是一个简单的判断手指滑动方向让页面滚动一屏的高度。
先来看代码:
$(&body&).on(&touchstart&, function(e) {
e.preventDefault();
startX = e.originalEvent.changedTouches[0].pageX,
startY = e.originalEvent.changedTouches[0].pageY;
$(&body&).on(&touchmove&, function(e) {
e.preventDefault();
moveEndX = e.originalEvent.changedTouches[0].pageX,
moveEndY = e.originalEvent.changedTouches[0].pageY,
X = moveEndX - startX,
Y = moveEndY - startY;
if ( X & 0 ) {
alert(&left 2 right&);
else if ( X & 0 ) {
alert(&right 2 left&);
else if ( Y & 0) {
alert(&top 2 bottom&);
else if ( Y & 0 ) {
alert(&bottom 2 top&);
alert(&just touch&);
判断很简单,touchmove的最后坐标减去touchstart的起始坐标,X的结果如果正数,则说明手指是从左往右划动;X的结果如果负数,则说明手指是从右往左划动;Y的结果如果正数,则说明手指是从上往下划动;Y的结果如果负数,则说明手指是从下往上划动。
这再逻辑上没有任何问题。但在实际操作中,手指的上下滑动其实是很难直上直下的,只要稍微有点斜,就会被X轴的判断先行接管。
那么接下来我们就需要梁逸峰同学附体,加上特别的判断技巧:
$(&body&).on(&touchstart&, function(e) {
e.preventDefault();
startX = e.originalEvent.changedTouches[0].pageX,
startY = e.originalEvent.changedTouches[0].pageY;
$(&body&).on(&touchmove&, function(e) {
e.preventDefault();
moveEndX = e.originalEvent.changedTouches[0].pageX,
moveEndY = e.originalEvent.changedTouches[0].pageY,
X = moveEndX - startX,
Y = moveEndY - startY;
if ( Math.abs(X) & Math.abs(Y) && X & 0 ) {
alert(&left 2 right&);
else if ( Math.abs(X) & Math.abs(Y) && X & 0 ) {
alert(&right 2 left&);
else if ( Math.abs(Y) & Math.abs(X) && Y & 0) {
alert(&top 2 bottom&);
else if ( Math.abs(Y) & Math.abs(X) && Y & 0 ) {
alert(&bottom 2 top&);
alert(&just touch&);
增加的判断也很简单,无非就是判断哪个的差值比较大。这样一来基本上就不会出错了。测试例子可以访问这里手机端html5触屏事件(touch事件)
touchstart:触摸开始的时候触发
touchmove:手指在屏幕上滑动的时候触发
touchend:触摸结束的时候触发
而每个触摸事件都包括了三个触摸列表,每个列表里包含了对应的一系列触摸点(用来实现多点触控):
touches:当前位于屏幕上的所有手指的列表。
targetTouches:位于当前DOM元素上手指的列表。
changedTouches:涉及当前事件手指的列表。
每个触摸点由包含了如下触摸信息(常用):
identifier:一个数值,唯一标识触摸会话(touch&session)中的当前手指。一般为从0开始的流水号(android4.1,uc)
target:DOM元素,是动作所针对的目标。
pageX/pageX/clientX/clientY/screenX/screenY:一个数值,动作在屏幕上发生的位置(page包含滚动距离,client不包含滚动距离,screen则以屏幕为基准)。 
radiusX/radiusY/rotationAngle:画出大约相当于手指形状的椭圆形,分别为椭圆形的两个半径和旋转角度。初步测试浏览器不支持,好在功能不常用,欢迎大家反馈。
var&obj&=&document.getElementByIdx_x('id');
obj.addEventListener('touchmove',&function(event)&{
&&&&&//&如果这个元素的位置内只有一个手指的话
&&&&if&(event.targetTouches.length&==&1)&{
    &event.preventDefault();//&阻止浏览器默认事件,重要&
&&&&&&&&var&touch&=&event.targetTouches[0];
&&&&&&&&//&把元素放在手指所在的位置
&&&&&&&&obj.style.left&=&touch.pageX-50&+&'px';
&&&&&&&&obj.style.top&=&touch.pageY-50&+&'px';
},&false);
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 微信浏览器 touchend 的文章

 

随机推荐