通过滑动翻页效果实现和移动端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 相关文章推荐
javascript中利用数组实现的循环队列代码
Jan 24 Javascript
js调试工具console.log()方法查看js代码的执行情况
Aug 08 Javascript
js生成的验证码的实现与技术分析
Sep 17 Javascript
jQuery获取iframe的document对象的方法
Oct 10 Javascript
Javascript访问器属性实例分析
Dec 30 Javascript
js计算文本框输入的字符数
Oct 23 Javascript
简单实现Bootstrap标签页
Aug 09 Javascript
利用babel将es6语法转es5的简单示例
Dec 01 Javascript
实例学习JavaScript读取和写入cookie
Jan 29 Javascript
微信小程序如何利用getCurrentPages进行页面传值
Jul 01 Javascript
javascript实现摄像头拍照预览
Sep 30 Javascript
vue项目中使用bpmn-自定义platter的示例代码
May 11 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
PHP中OpenSSL加密问题整理
2017/12/14 PHP
模仿jQuery each函数的链式调用
2009/07/22 Javascript
JavaScript学习笔记(十)
2010/01/17 Javascript
javascript学习笔记(七)利用javascript来创建和存储cookie
2011/04/08 Javascript
理解javascript回调函数
2014/12/28 Javascript
jQuery实现ichat在线客服插件
2014/12/29 Javascript
jQuery浏览器CSS3特写兼容实例
2015/01/19 Javascript
理解javascript闭包
2015/12/15 Javascript
jQuery代码实现对话框右上角菜单带关闭×
2016/05/03 Javascript
javascript实现获取指定精度的上传文件的大小简单实例
2016/10/25 Javascript
完美解决input[type=number]无法显示非数字字符的问题
2017/02/28 Javascript
BootStrap表单宽度设置方法
2017/03/10 Javascript
jQuery实现导航栏头部菜单项点击后变换颜色的方法
2017/07/19 jQuery
基于vue中css预加载使用sass的配置方式详解
2018/03/13 Javascript
JS中判断某个字符串是否包含另一个字符串的五种方法
2018/05/03 Javascript
vue引入静态js文件的方法
2020/06/20 Javascript
详解vue父子组件状态同步的最佳方式
2020/09/10 Javascript
详解ES6中class的实现原理
2020/10/03 Javascript
深入理解Python3中的http.client模块
2017/03/29 Python
让Python更加充分的使用Sqlite3
2017/12/11 Python
Python求正态分布曲线下面积实例
2019/11/20 Python
Python实现FLV视频拼接功能
2020/01/21 Python
python3连接MySQL8.0的两种方式
2020/02/17 Python
Python变量格式化输出实现原理解析
2020/08/06 Python
施华洛世奇加拿大官网:SWAROVSKI加拿大
2018/06/03 全球购物
DOUGLAS荷兰:购买香水和化妆品
2020/10/24 全球购物
机械绘图员岗位职责
2013/11/19 职场文书
大二法学专业职业生涯规划范文
2014/02/12 职场文书
大学学习个人的自我评价
2014/02/18 职场文书
审计局2014法制宣传日活动总结
2014/11/01 职场文书
采购员岗位职责范本
2015/04/07 职场文书
婚庆开业庆典主持词
2015/06/30 职场文书
2016年小学生清明节广播稿
2015/12/17 职场文书
2016年师德先进个人事迹材料
2016/02/29 职场文书
GoLang中生成UUID唯一标识的实现
2021/05/08 Golang
详解OpenCV曝光融合
2022/04/29 Python