【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行为,看到这里。。笔者内心也是深感“这是一个何其大的大乌龙啊”。。不过也是因为平日中太过偏重解决问题,而没有仔细研究解决问题的方法的原理与机制。

  虽然各司其职是现代化大分工的基本诉求,但是有的时候知其所以然才能更有价值的提高我们的工作效率,对于我们解决实际问题,也是颇有裨益的。

时间: 2024-10-23 06:57:21

【js】再谈移动端的模态框实现的相关文章

[转]再谈移动端Web屏幕适配

一个多月前水了一篇移动web屏幕适配方案,当时噼里啪啦的写了一通,自我感觉甚是良好.不过最近又有一些新的想法,和之前的有一些不同. 先说一下淘宝的方案,感觉现在好多的适配方案都是受了它的影响,上周六看了winter在一个会议的分享,讲到了这个方案.现在你谷歌一下移动web适配,绝对可以看到很多类似的,切活动页的童鞋都忍不住试一把.这些方案和我的博客写的其实还是相似的,就是抛弃了那种viewport直接缩放,然后给定html的初始font-size值,使用rem这个单位. 在屏幕的设备像素比上,也

移动端之模态框滚动穿透问题

相信各位小伙伴利用H5做移动端应用时,在写弹出层时肯定用到过滚动穿透问题,虽然页面加了背景遮罩,滑动页面时,如若页面滚在滚动条,底部仍然可以滑动.遇到这种问题,第一想到的可能是能否监听弹出层的显示与隐藏,在监听函数中做处理:一般有以下两种处理方式: 1.禁止body的touchmove事件 1 function handler(e){e.preventDefault();} 2 3 export function closeTouch(){ 4 document.getElementsByTag

js学习之--Bootstrap Modals(模态框)

http://www.runoob.com/bootstrap/bootstrap-v2-modal-plugin.html http://outofmemory.cn/bootstrap/tutorial/bootstrap-modal-plugin.html 版权声明:本文为博主原创文章,未经博主允许不得转载.

MVC中调用模态框之后导致JS失效

今天在工作中碰到一个页面调用模态框之后,页面原来的JS失效的问题,由于前台经验较少,折腾了一天... 问题描述是这样,在页面,有两个下拉列表框A和B,做了下拉列表框联动,有一个button按钮会调用模态框,刚进入页面联动是好用的,所以联动的JS 代码没问题,点击模态框之后,JS失效. 上图是下拉列表框联动的JS 经过不懈的调试(其实就是各种瞎试)以及询问老大哥(这个才是解决之道),终于发现了问题所在: 页面刚加载进来的时候联动JS好用,是因为直接加载了JS,调用模态框之后,在success回调函

JS /CSS 实现模态框(注册和登录组件)

1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>JS/CSS 注册表单(模态框设置)</title> 6 <style> 7 8 input[type=email], input[type=password] { 9 width: 100%; 10 padding: 12px 20px; 11 margin: 8

Bootstrap 模态框(Modal)插件

http://www.runoob.com/bootstrap/bootstrap-modal-plugin.html 模态框(Modal)是覆盖在父窗体上的子窗体.通常,目的是显示来自一个单独的源的内容,可以在不离开父窗体的情况下有一些互动.子窗体可提供信息.交互等. 如果您想要单独引用该插件的功能,那么您需要引用 modal.js.或者,正如 Bootstrap 插件概览 一章中所提到,您可以引用 bootstrap.js 或压缩版的 bootstrap.min.js. 用法 您可以切换模态

Bootstrap 实例 - 模态框(Modal)插件

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Bootstrap 实例 - 模态框(Modal)插件</title> <link rel="stylesheet" href="https://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.m

Bootstrap Modals(模态框)

http://www.runoob.com/bootstrap/bootstrap-v2-modal-plugin.html 描述 Bootstrap Modals(模态框)是使用定制的 Jquery 插件创建的.它可以用来创建模态窗口丰富用户体验,或者为用户添加实用功能.您可以在 Modals(模态框)中使用 Popover(弹出框)和 Tooltip(工具提示插件). 在本教程中,将通过一些实例和解释来讨论如何使用 Bootstrap 创建模态窗口.同时,我们也会讨论用于定制的各种可用选项.

模态框无法弹出的问题

问题起因: 昨晚写到了一个模态框,用到了bootstrap和jquery,依赖的js已经复制到项目中,并在Jsp页面上进行了引用,最初的引用如下: <srcipt src="$${pageContext.request.contextPath }/js/jquery-3.3.1.min.js"></srcipt> <link href="$${pageContext.request.contextPath }/css/bootstrap.min.