【js】再谈移动端的模态框实现
移动端模态框的机制因为与PC的模态框机制一直有所区别,一直是许多新人很容易踩坑的地方,最近笔者作为一条老咸鱼也踩进了一个新坑中,真是平日里代码读得太粗略,故而写上几笔,以儆效尤。
故事的起因是这样的,兄弟团的童鞋的页面出现了模块框内需要滚动元素的需求,但是实际情况是他调试了很久,却没有找到确定的解决问题,这也引起了笔者的注意,虽然有现成的组件,但是因为相关代码是有一些历史的了,并没有迁移,于是笔者就和他以前联调了一番。
我们知道常见的pc端模块框阻止滚动的方式是在html或者body标签上添加overflow:hidden,以及margin:0等来实例上将页面置为一个不可滚动的页面,而在移动端,则需要我们手动的阻止相关dom的touchmove事件的冒泡,来达到目的,示意代码如下:
/*html code*/
<div class='modal'>
<div class="overlay" id="overlayId"></div>
<div class='modal-content'id='YourModalContentId'></div>
</div>
/*js code*/
function addEvt(dom){
dom && dom.addEventListener('touchmove', onTouchMove);
}
function onTouchMove(e){
e.preventDefault();
}
function fn(){
let overlayDom = document.querySelector('#overlayId');
let modalDom = document.querySelector('#YourModalContentId');
}
阻止所有手指可以碰触的元素的touchmove事件冒泡(以免引起比如在微信中的view滚动,也可避免不能触发click事件,因为click事件不需要touchmove,只需要touchstart和touchend),这是其中的原理,然后实际例子稍微复杂了一点,因为实际场景需要modalcontent内部的dom滚动,一般做法是引入iscroll用touchmove事件来模拟滚动事件,但是这位童鞋做了常规操作之后得到了不同的结论,里面的dom依然不能滚动,经过笔者和他仔细的比对之后,发现基本上只有一行代码的不同:
/*html code*/
<div class='modal'>
<div class="overlay" id="overlayId"></div>
<div class='modal-content'id='YourModalContentId'>
<ul>
...
</ul>
</div>
</div>
/*js code*/
function addEvt(dom){
dom && dom.addEventListener('touchmove', onTouchMove);
}
function onTouchMove(e){
e.preventDefault();
e.stopPropagation();
}
function fn(){
let overlayDom = document.querySelector('#overlayId');
let modalDom = document.querySelector('#YourModalContentId');
let scroller = new IScroll(modalDom, YourOptions);
}
就是上面标红的那句话,但是正常情况下stopPropagation才是阻止事件冒泡,笔者开始也以为应该是没有关系的,但是经过反复测试后发现。。没有那句话,内部dom的滚动没有任何问题,但是有了那句话之后,内部则不能滚动了。。细细思考之后,笔者觉着。。多半是iscroll内部的实现机制了。。
于是读了下iscroll的源码,发现iscroll在initEvents时做了一个神奇的操作:
_initEvents: function(remove) {
var eventType = remove ? utils.removeEvent : utils.addEvent,
target = this.options.bindToWrapper ? this.wrapper : window; eventType(window, 'orientationchange', this);
eventType(window, 'resize', this); if (this.options.click) {
eventType(this.wrapper, 'click', this, true);
} if (!this.options.disableMouse) {
eventType(this.wrapper, 'mousedown', this);
eventType(target, 'mousemove', this);
eventType(target, 'mousecancel', this);
eventType(target, 'mouseup', this);
} if (utils.hasPointer && !this.options.disablePointer) {
eventType(this.wrapper, 'MSPointerDown', this);
eventType(target, 'MSPointerMove', this);
eventType(target, 'MSPointerCancel', this);
eventType(target, 'MSPointerUp', this);
} if (utils.hasTouch && !this.options.disableTouch) {
eventType(this.wrapper, 'touchstart', this);
eventType(target, 'touchmove', this);
eventType(target, 'touchcancel', this);
eventType(target, 'touchend', this);
} eventType(this.scroller, 'transitionend', this);
eventType(this.scroller, 'webkitTransitionEnd', this);
eventType(this.scroller, 'oTransitionEnd', this);
eventType(this.scroller, 'MSTransitionEnd', this);
}
在适用方没有强制绑定wrapper的情况下,touchstart、touchmove、touchend的target都是window!看到这里聪明的你也许已经反应过来了,这就是为什么我们平常写到touch事件的代码在移动出了dom的范围之后不能正常的释放,而iscroll的可以。。因为除了touchstart之外,其他的事件都是加在全局的window对象上的,而我们遇到的这个实际问题又恰恰使用了touchmove事件,事件触发的层级关系变成了:
/*dom 示意*/
window //iscroll 处理touchmove
-html
-body
--modal
---overlay //阻止事件冒泡
---modal-content //阻止事件冒泡
----iScrollElement
我们需要等待事件冒泡到了window上,才能正常的处理iscrollElement的touchmove行为,看到这里。。笔者内心也是深感“这是一个何其大的大乌龙啊”。。不过也是因为平日中太过偏重解决问题,而没有仔细研究解决问题的方法的原理与机制。
虽然各司其职是现代化大分工的基本诉求,但是有的时候知其所以然才能更有价值的提高我们的工作效率,对于我们解决实际问题,也是颇有裨益的。
【2017.09.27】更新
最近笔者的android系统更新到了7.0...然后发现以上的通用移动端滚动模态窗的解决方案失效了。。问题似乎出在preventDefault不再按照我们期望的方式工作了。。于是需要更新一下实现:
由于内部的滚动一定会传播到上层,那么解决思路就只能是将上层的滚动条件彻底移除了,即需要在模态框展示的时候先标记当前的window.scrollY(这里我们只考虑上层只有一个滚动条的情况),然后直接设置body的样式,将其overflow设置为hidden,物理上禁止上层容器的滚动;
然后,对于模态框上的滚动容器我们不再需要使用touch事件模拟滚动了,可以直接使用原生的滚动条;
最后,在模态框消失的时候,我们需要手动还原window的滚动条为之前的状态(注意这里其实会有一些体验问题,但是我们可以使用先还原滚动条状态再隐藏模态框的hack来避免体验问题)。
【2017.11.08】更新
最近笔者在参考其他框架实现的时候发现,支付宝的antd-mobile并没有笔者之前在android所遇到的问题,研究代码发现,因为antd引用了另一个滚动的实现——scroller,再往下看了下实现,发现是因为iscroll的touch事件识别虽然是放在传入的容器上到,但是具体的行为为了体验的优化,却将touchmove和touchend事件添加到了window(如前文所述),而android 7似乎对这种操作的支持并不是很友好,导致了touchmove事件最先响应在了外层容器。。导致原本容器内部滚动的事件不能被正确处理。
其实,没想到iscroll本来一个为了优化体验而想出的小技巧,到了android 7时代,反而弄巧成拙。
【js】再谈移动端的模态框实现的更多相关文章
- 再谈移动端Web屏幕适配
一个多月前水了一篇移动web屏幕适配方案,当时噼里啪啦的写了一通,自我感觉甚是良好.不过最近又有一些新的想法,和之前的有一些不同. 先说一下淘宝的方案,感觉现在好多的适配方案都是受了它的影响,上周六看 ...
- js学习之--Bootstrap Modals(模态框)
http://www.runoob.com/bootstrap/bootstrap-v2-modal-plugin.html http://outofmemory.cn/bootstrap/tutor ...
- MVC中调用模态框之后导致JS失效
今天在工作中碰到一个页面调用模态框之后,页面原来的JS失效的问题,由于前台经验较少,折腾了一天... 问题描述是这样,在页面,有两个下拉列表框A和B,做了下拉列表框联动,有一个button按钮会调用模 ...
- 使用bootstrap的JS插件实现模态框效果
在上一篇文章中,我们使用 js+css 实现了模态框效果,在理解了模态框的基本实现方法和实现效果后,我们就要寻找更快捷的方法,又快又好的来完成模态框开发需求,从而节约时间,提高效率.一个好的轮子,不仅 ...
- JS /CSS 实现模态框(注册和登录组件)
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 基于bootstrap模态框的日期选择器
近来由于工作需求,以bootstrap模态框+DIV+CSS+JS做了一个适用于移动端的日期选择器,能够满足多样的需求,目前处于第一个版本,后续可能会继续更新.废话不多说,直接进入制作过程. 首先,需 ...
- 解决Ueditor在bootstarp 模态框中全屏问题
基本的一些配置就不说了.先说一下要注意的问题:首先是zIndex的设置.记住最好都显示设置模态框和ueditor的zIndex.理清他们的层叠关系. 特别是用到ueditor里面的图片上传功能的更要设 ...
- Bootstrap 模态框(Modal)插件
原文链接:http://www.runoob.com/bootstrap/bootstrap-modal-plugin.html Bootstrap 模态框(Modal)插件 模态框(Modal)是覆 ...
- Bootstrap <基础三十二>模态框(Modal)插件
模态框(Modal)是覆盖在父窗体上的子窗体.通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动.子窗体可提供信息.交互等. 如果您想要单独引用该插件的功能,那么您需要引用 ...
随机推荐
- hdu - 1068 Girls and Boys (二分图最大独立集+拆点)
http://acm.hdu.edu.cn/showproblem.php?pid=1068 因为没有指定性别,所以要拆点,把i拆分i和i’ 那么U=V-M (M是最大匹配,U最大独立集,V是顶点数) ...
- 创建Django项目(六)——模板
2013-08-07 22:42:30| 1.设置模板路径 打开 settings.py 文件,修改 TEMPLATE_DIRS 内容,指向模板存放的绝对路径,而不 ...
- 【SQL Server 学习系列】-- 随机生成日期时间的SQL脚本
DECLARE @dt1 DATETIME,@dt2 DATETIME,@a BIGINT,@b BIGINT SET @dt1='2010-01-01'--开始日期 SET @dt2='2010-0 ...
- MongoDB小结10 - update【upsert】
upsert是一个选项,它是update的第三个参数,并不是一个方法.它是一种特殊的更新,要是没有文档符合匹配,那么它就会根据条件和更新文档为基础,创建新的文档,如有匹配,则正常更新.咱们之前见到的所 ...
- MySQL架构优化实战系列4:SQL优化步骤与常用管理命令
- CodeForces484A Bits(贪心)
CodeForces484A Bits(贪心) CodeForces484A 题目大意:给出范围[A.B].期望你给出某个数X满足X属于[A,B],而且X转成二进制的1的个数最多.假设有多个给出最小的 ...
- Android怎样监听蓝牙耳机的按键事件
Android怎样监听蓝牙耳机的按键事件 写在前面: 直接想要代码非常easy,你直接把滚动栏拉到最底端就能够看到.假设想要十分地了解为什么,那就依照我规划的一步一步来理解.下面測试环境以手头上有的「 ...
- 三元表达式之理解/jquery源代码分析之$.inArray实现
每次看到三元表达式就会惶惶然分不清怎样读,正如语文中的断句一样,jquery源代码中的三元表达式更是不知怎样断句. 附jquery中的inArray实现. 大家熟悉jquery的应该都不陌生inArr ...
- Linux进程间通信 共享内存+信号量+简单样例
每个进程都有着自己独立的地址空间,比方程序之前申请了一块内存.当调用fork函数之后.父进程和子进程所使用的是不同的内存. 因此进程间的通信,不像线程间通信那么简单.可是共享内存编程接口能够让一个进程 ...
- 解决Hibernate4执行update操作,不更新数据的问题
后台封装java对象,使用hibernate4再带的update,执行不更新数据,不报错. 下面贴出解决方法: 失败的方法 hibernate自带update代码:(失效) Session sessi ...