通过滑动翻页效果实现和移动端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的ajax从纯真网(cz88.net)获取IP地址对应地区名
Dec 02 Javascript
FileUpload 控件 禁止手动输入或粘贴的实现代码
Apr 07 Javascript
开发 Internet Explorer 右键功能表(ContextMenu)
Jul 03 Javascript
页面载入结束自动调用js函数示例
Sep 23 Javascript
浅谈js在html中的加载执行顺序,多个jquery ready执行顺序
Nov 26 Javascript
vue2.0结合Element实现select动态控制input禁用实例
May 12 Javascript
浅谈struts1 &amp; jquery form 文件异步上传
May 25 jQuery
vue复合组件实现注册表单功能
Nov 06 Javascript
web前端vue之CSS过渡效果示例
Jan 10 Javascript
vue实现与安卓、IOS交互的方法
Nov 02 Javascript
浅谈vuex actions和mutation的异曲同工
Dec 13 Javascript
vue父子组件的通信方法(实例详解)
Nov 10 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中使用与Perl兼容的正则表达式
2006/11/26 PHP
PHP+jQuery 注册模块的改进(一):验证码存入SESSION
2014/10/14 PHP
6个超实用的PHP代码片段
2015/08/10 PHP
PHP响应post请求上传文件的方法
2015/12/17 PHP
PHP获取IP地址所在地信息的实例(使用纯真IP数据库qqwry.dat)
2016/11/15 PHP
由prototype_1.3.1进入javascript殿堂-类的初探
2006/11/06 Javascript
JQuery AJAX提交中文乱码的解决方案
2010/07/02 Javascript
读JavaScript DOM编程艺术笔记
2011/11/15 Javascript
javascript检测页面是否缩放的小例子
2013/05/16 Javascript
jQuery给多个不同元素添加class样式的方法
2015/03/26 Javascript
基于ajax实现文件上传并显示进度条
2015/08/03 Javascript
angularjs 表单密码验证自定义指令实现代码
2016/10/27 Javascript
纯JS实现图片验证码功能并兼容IE6-8(推荐)
2017/04/19 Javascript
vue.js路由跳转详解
2017/08/28 Javascript
详解angularjs popup-table 弹出框表格指令
2017/09/20 Javascript
json数据传到前台并解析展示成列表的方法
2018/08/06 Javascript
微信小程序中转义字符的处理方法
2019/03/28 Javascript
微信小程序实现侧边分类栏
2019/10/21 Javascript
vue-router之实现导航切换过渡动画效果
2019/10/31 Javascript
JS中==、===你分清楚了吗
2020/03/04 Javascript
基于node+websocket+html实现腾讯课堂聊天室聊天功能
2020/03/04 Javascript
解决vant-UI库修改样式无效的问题
2020/11/03 Javascript
[03:21]【TI9纪实】Old Boys
2019/08/23 DOTA
python的urllib模块显示下载进度示例
2014/01/17 Python
Python实现的简单发送邮件脚本分享
2014/11/07 Python
Python判断操作系统类型代码分享
2014/11/22 Python
python读取文本绘制动态速度曲线
2018/06/21 Python
python爬虫 基于requests模块发起ajax的get请求实现解析
2019/08/20 Python
pytorch标签转onehot形式实例
2020/01/02 Python
Python标准库shutil模块使用方法解析
2020/03/10 Python
Django生成数据库及添加用户报错解决方案
2020/10/09 Python
丹尼尔惠灵顿手表天猫官方旗舰店:Daniel Wellington
2017/08/25 全球购物
师说教学反思
2014/02/07 职场文书
环保建议书600字
2014/05/14 职场文书
应聘护士求职信
2014/07/21 职场文书
破坏寝室公物检讨书
2014/11/17 职场文书