通过滑动翻页效果实现和移动端click事件问题


Posted in Javascript onJanuary 26, 2021

前述

本文很短~
主要是为了总结和讲述移动端click和js事件机制导致的一个问题。
(:咳咳,其实几句话就能写完的还要水一篇文章,不愧是我…


正文

最近做了一个小活动,里面要用到一个效果:滑动翻页。大概是这样的:

通过滑动翻页效果实现和移动端click事件问题

<!-- HTML代码 -->
<div class="page-container">
	<div class="page" style="background: #dc3b26">1</div>
	<div class="page" style="background: #f3b03d">2</div>
	<div class="page" style="background: #44a2f8">3</div>
</div>

在css中,首先因为是滑动翻页,所以我们要保证“始终只有一屏”,这个可以放在全局样式表里控制,然后是其中的“每一页”都要占满父元素 —— 这里其实用了“div是块元素,无外力情况下竖直排列”的特性。

/** css样式 */
html,
body{
	margin: 0;
	padding: 0;
	width: 100%;
	height: 100vh;
	overflow: hidden;
	font-size: 60px;
}
.page-container{
	width: 100%;
	height: 100%;
}
.page-container .page{
	width: 100%;
	height: 100%;
}

其JS实现也很简单:因为在移动端,所以使用了touchstarttouchmovetouchend事件来实现手势滑动功能:

  • start(刚按下)时记录此时的手指位置——作为初始值;
  • 在move(触摸滑动)时根据实时的手指位置和初始手指位置变量实现要求判断,在本文场景为代表的场景下这一步一般还要求出“移动距离”实时赋值;
  • 在end(手指离开)时(也有直接在move时进行的)进行收尾工作——比如:图片滑动完全划过去、元素跑到结束位置、将事件监听取消;
// 这里是控制全局js的文件
// 全局阻止浏览器默认行为
document.addEventListener("touchstart",function(e){
	if(e.cancelable){
		e.preventDefault();
	}
},{passive: false})
// {passive: false}就是告诉前面可能有需要重置行为的代码
document.addEventListener("touchmove",function(e){
	if(e.cancelable){
		e.preventDefault();
	}
},{passive: false})
// JavaScript代码
let curPageIndex=0;
let pageContainer=document.querySelector(".page-container");
let pageNumber=pageContainer.children.length;  //页面的数量
// 文档的视窗高度(这里就是一屏的高度)
let cHeight=document.documentElement.clientHeight;
// 设置页面容器的margin-top为合适的值,让其显示在视野中
function toPage(){
	pageContainer.style.transition="all .5s ease";
	pageContainer.style.marginTop=-curPageIndex*cHeight+"px";
	
	// 变化完成后去掉过渡效果 !
	pageContainer.addEventListener("transitionend",function(){
		pageContainer.style.transition="none";
	},{once:true})
}
toPage()

pageContainer.ontouchstart=function(e){
	let y=e.changedTouches[0].clientY;  //手指按下的纵坐标
	pageContainer.ontouchmove=function(e){
		let dis=e.changedTouches[0].clientY-y;  //计算距离
		// 计算page-container的margin-top
		let mtop=-curPageIndex*cHeight+dis
		if(mtop>0){
			mtop=0;
		}else if(mtop<-(pageNumber-1)*cHeight){
			mtop=-(pageNumber-1)*cHeight;
		}
		// 实时改变位置
		pageContainer.style.marginTop=mtop+"px";
	}
	pageContainer.ontouchend=function(e){
		let dis=e.changedTouches[0].clientY-y;
		// 如果滑动距离实在太短,就回到滑动前的位置状态
		if(Math.abs(dis)<=60){
			toPage()
		}else if(dis>0 && curPageIndex>0){
			curPageIndex--;
			toPage()
		}else if(dis<0 && curPageIndex<pageNumber-1){
			curPageIndex++;
			toPage()
		}
		
		// 手指离开后,取消监听事件
		pageContainer.ontouchmove=null;
		pageContainer.ontouchend=null;
	}
}

至此,功能上似乎很完美。但这时候,我们在第一个page里添加一个button:

<div class="page" style="background: #dc3b26">
	<button onclick="alert('哈哈哈')" class="but">click me!</button>
	1
</div>

然后到页面上查看效果:

通过滑动翻页效果实现和移动端click事件问题

无效!

这是因为在上方全局js文件里我们加了“阻止浏览器默认事件”的代码。
——在移动端浏览器中,click事件和mousestart事件是同时被触发的。因为移动端浏览器是没有click事件的,它是由mouse事件模拟的! :也正是这个原因,才有了所谓的“移动端浏览器300ms延迟”的问题 1 。
——还有就是,在微信自带的浏览器中,有一个“触顶下拉回弹”的操作,这其实是不应该的。它也属于浏览器默认事件。
所以一般我们需要禁止掉这些东西。

但是如上面所示,全部禁止掉总会造成一些困扰,怎么办?
H5提供了“自定义属性”,针对本文方法,我们完全可以 —— 在全局事件里检测当前触发的元素有没有某一个自定义属性,如果有,就什么也不拦截;否则就执行禁止默认行为的代码:
比如

<button data-default="true" onclick="alert('哈哈哈')" class="but">click me!</button>

将上面“控制全局js的文件”内容改为如下:

// 全局阻止浏览器默认行为
document.addEventListener("touchstart",function(e){
	if(e.target.dataset.default){
		return;
	}
	if(e.cancelable){
		e.preventDefault();
	}
},{passive: false})
document.addEventListener("touchmove",function(e){
	if(e.target.dataset.default){
		return;
	}
	if(e.cancelable){
		e.preventDefault();
	}
},{passive: false})

就OK了:

通过滑动翻页效果实现和移动端click事件问题


其实还有另一种“解法”:既然上面说了,移动端click实际上是通过mouse事件模拟的,那么我们可以从mousestart事件入手;又因为button元素是“第一个页面”内的(子)元素,所以可以用阻止事件冒泡

<!-- button就是普通的button -->
<button class="but">click me!</button>
document.querySelector(".but").addEventListener("touchstart",function(e){
	e.stopPropagation();
	alert('噶哈哈');
},false)

通过滑动翻页效果实现和移动端click事件问题

关于捕获和冒泡→

我们首先要知道的是:当我们鼠标按下一个按钮时,并不是“点击了一个按钮”,而是在这个区域内,鼠标(上的按键)被按下,操作系统和浏览器把这个信息对应到了“按钮”所在区域并触发其逻辑。
事实上鼠标点击并没有位置信息,是操作系统一直在监听鼠标移动,根据累积的位移计算出来的坐标,将其传给浏览器。
那么,把这个坐标转换为具体的元素上的事件的过程,就可称作“捕获”。那“冒泡”呢?这个不好解释,但有一点想必你是明白的:当你按下电视开关时,你也按到了电视!
这就是很多文章会讲到的“冒泡过程由内向外,捕获过程由外向内”,或者说是“洋葱模型”。
还有一点就是:事件addEventListener的第三个参数 true/false ,即为“是捕获/冒泡”。(别多想,这只是浏览器提供的事件模型之一。无论是否监听,在一个事件发生时,捕获和冒泡总是先后发生的)

到此这篇关于滑动翻页效果实现和移动端click事件问题的文章就介绍到这了,更多相关滑动翻页效果内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
几行代码轻松搞定jquery实现flash8类似的连接效果
May 03 Javascript
Jquery截取中文字符串的实现代码
Dec 22 Javascript
鼠标右击事件代码(asp.net后台)
Jan 27 Javascript
同时使用n个window onload加载实例介绍
Apr 25 Javascript
AngularJS中实现显示或隐藏动画效果的方式总结
Dec 31 Javascript
jquery插件Jplayer使用方法简析
Apr 22 Javascript
利用jQuery实现一个简单的表格上下翻页效果
Mar 14 Javascript
Vue中的v-cloak使用解读
Mar 27 Javascript
jQuery层级选择器_动力节点节点Java学院整理
Jul 04 jQuery
微信小程序代码上传、审核发布小程序
May 18 Javascript
微信小程序实现消息框弹出动画
Apr 18 Javascript
JavaScript选择器函数querySelector和querySelectorAll
Nov 27 Javascript
全面解析js中的原型,原型对象,原型链
Jan 25 #Javascript
js中实现继承的五种方法
Jan 25 #Javascript
Vue中的nextTick作用和几个简单的使用场景
Jan 25 #Vue.js
Vue使用Ref跨层级获取组件的步骤
Jan 25 #Vue.js
javascript实现点击产生随机图形
Jan 25 #Javascript
如何在Vue项目中添加接口监听遮罩
Jan 25 #Vue.js
json.stringify()与json.parse()的区别以及用处
Jan 25 #Javascript
You might like
《忧国的莫里亚蒂》先导宣传图与STAFF公开
2020/03/04 日漫
php实现JWT验证的实例教程
2020/11/26 PHP
javascript实现划词标记+划词搜索功能
2007/03/06 Javascript
js substr、substring和slice使用说明小记
2011/09/15 Javascript
javascript椭圆旋转相册实现代码
2012/01/16 Javascript
javascript中with()方法的语法格式及使用
2014/08/04 Javascript
js使用循环清空某个div中的input标签值
2014/09/29 Javascript
JS获得选取checkbox整行数据的方法
2015/01/28 Javascript
JS定义网页表单提交(submit)的方法
2015/03/20 Javascript
JavaScript:Array类型全面解析
2016/05/19 Javascript
JavaScript简单下拉菜单特效
2016/09/13 Javascript
JavaScript实现自动切换图片代码
2016/10/11 Javascript
Angularjs实现上传图片预览功能
2017/09/01 Javascript
用 Vue.js 递归组件实现可折叠的树形菜单(demo)
2017/12/25 Javascript
vue实现同一个页面可以有多个router-view的方法
2018/09/20 Javascript
Three.JS实现三维场景
2018/12/30 Javascript
用Javascript实现发送短信验证码间隔功能
2021/02/08 Javascript
[50:54]完美世界DOTA2联赛 GXR vs IO 第三场 11.07
2020/11/10 DOTA
python统计字符串中指定字符出现次数的方法
2015/04/04 Python
使用 Python 玩转 GitHub 的贡献板(推荐)
2019/04/04 Python
pandas DataFrame 行列索引及值的获取的方法
2019/07/02 Python
使用Python进行中文繁简转换的实现代码
2019/10/18 Python
pycharm显示远程图片的实现
2019/11/04 Python
Vs Code中8个好用的python 扩展插件
2020/10/12 Python
python基于win32api实现键盘输入
2020/12/09 Python
苹果中国官方网站:Apple中国
2016/07/22 全球购物
客户代表实习人员自我鉴定
2013/09/27 职场文书
高中运动会入场词
2014/02/14 职场文书
正风肃纪剖析材料
2014/02/18 职场文书
公职人员索取回扣检举信
2014/04/04 职场文书
数控专业毕业生求职信
2014/06/12 职场文书
企业党员个人自我评价
2014/09/20 职场文书
电力安全教育培训心得体会
2016/01/11 职场文书
电力安全学习心得体会
2016/01/18 职场文书
2016教师读书思廉心得体会
2016/01/23 职场文书
2016年优秀团支部事迹材料
2016/02/26 职场文书