通过滑动翻页效果实现和移动端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 相关文章推荐
让Firefox支持event对象实现代码
Nov 07 Javascript
Extjs TriggerField在弹出窗口显示不出问题的解决方法
Jan 08 Javascript
跨浏览器开发经验总结(四) 怎么写入剪贴板
May 13 Javascript
aspx中利用js实现确认删除代码
Jul 22 Javascript
Javascript之this关键字深入解析
Nov 12 Javascript
javascript类型转换示例
Apr 29 Javascript
jQuery Validation Plugin验证插件手动验证
Jan 26 Javascript
JS返回只包含数字类型的数组实例分析
Dec 16 Javascript
vue-router 源码实现前端路由的两种方式
Jul 02 Javascript
vue-router之nuxt动态路由设置的两种方法小结
Sep 26 Javascript
iview Upload组件多个文件上传的示例代码
Sep 30 Javascript
Vue中的$set的使用实例代码
Oct 08 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
jquery+php实现导出datatables插件数据到excel的方法
2015/07/06 PHP
PHP基于SMTP协议实现邮件发送实例代码
2017/04/27 PHP
利用php-cli和任务计划实现订单同步功能的方法
2017/05/03 PHP
PHP并发查询MySQL的实例代码
2017/08/09 PHP
thinkPHP5框架中widget的功能与用法详解
2018/06/11 PHP
Javascript注入技巧
2007/06/22 Javascript
解析DHTML,JavaScript,DOM,BOM以及WEB标准的描述
2013/06/19 Javascript
Jquery倒计时源码分享
2014/05/16 Javascript
浅谈JavaScript数据类型
2015/03/03 Javascript
js插件YprogressBar实现漂亮的进度条效果
2015/04/20 Javascript
jquery获取select选中值的方法分析
2015/12/22 Javascript
JS实现根据文件字节数返回文件大小的方法
2016/08/02 Javascript
老生常谈JQuery data方法的使用
2016/09/09 Javascript
可输入文字查找ajax下拉框控件 ComBox的实现方法
2016/10/25 Javascript
JavaScript 巧学巧用
2017/05/23 Javascript
微信小程序框架的页面布局代码
2019/08/17 Javascript
layui富文本编辑器前端无法取值的解决方法
2019/09/18 Javascript
vue实现将一个数组内的相同数据进行合并
2019/11/07 Javascript
vue Element左侧无限级菜单实现
2020/06/10 Javascript
JS中队列和双端队列实现及应用详解
2020/09/29 Javascript
JavaScript如何实现防止重复的网络请求的示例
2021/01/28 Javascript
浅谈python3.x pool.map()方法的实质
2019/01/16 Python
Python实现带下标索引的遍历操作示例
2019/05/30 Python
python中time库的实例使用方法
2019/10/31 Python
python验证码图片处理(二值化)
2019/11/01 Python
浅谈python opencv对图像颜色通道进行加减操作溢出
2020/06/03 Python
web页面录屏实现
2019/02/12 HTML / CSS
html5 video全屏播放/自动播放的实现示例
2020/08/06 HTML / CSS
西班牙在线药店:DosFarma
2020/03/28 全球购物
金蝶的一道SQL笔试题
2012/12/18 面试题
中药学自荐信
2014/06/15 职场文书
挂职锻炼个人总结
2015/03/05 职场文书
2015人事行政工作总结范文
2015/05/21 职场文书
大学生支教感言
2015/08/01 职场文书
经典法律座右铭(50句)
2019/08/15 职场文书
导游词之江南周庄
2019/12/06 职场文书