原生js实现模态窗🍀

作者: iamswf | 来源:发表于2018-12-09 14:05 被阅读19次

    最近在做一个基于zeptoh5项目,整个技术栈比较老旧,因此需要自己动手实现模态窗。现在已经是reactvue这些mvvm框架的天下了,请不要嘲笑我2018年还在使用zepto开发,我也是很无奈呀~。不过无论这些轮子框架发展到多么先进的地步,基础知识还是需要熟悉的嘛。本篇总结会介绍下模态窗的概念以及如何实现。

    什么是模态(modal)

    不知道大家对模态这个词怎么看,反正我最开始看到是一脸懵逼,不知道是什么意思,只是隐约觉得这种弹窗后面一般都有一个灰色蒙层。我是个喜欢追根溯源的人,一般碰到这种情况都会很纠结,非的弄明白为什么叫模态窗
    后来查了下,发现模态窗这个词是软件设计领域的词汇。咱们可以先看看普通的软件使用流程,在使用软件的时候,我们一般都会按照自己的思路以及软件内在的逻辑一步步操作,比如我们在使用一个购物系统,我们会按照我们对这个软件的固有理解来执行自己脑中的流程:选购商品,加入购物车,下单付款等等。这些流程可以说是我们使用软件时的一种正常操作状态
    模态其实来源于单词modal,表示一种特定模式的状态,那么是什么特定模式呢?其实是一种阻塞正常操作的模式。模态窗弹出时会使主窗口变的不可用,只有在模态窗内的操作完成后才能关闭模态窗并返回正常主窗口。关机模态窗更加详细的介绍可以参考维基百科Modal window

    cssfixed绝对定位

    这里再介绍下css中的绝对定位。为什么要介绍这个概念呢?因为我要实现的模态窗需要保证水平&垂直同时居中(大部分简单的模态窗都是水平、垂直居中的),其实水平&垂直同时居中的模态窗的实现很简单,就是利用cssfixed绝对定位来实现的。

    cssposition包括absolutefixedrelative以及static等定位方式,这里我们只介绍fixed定位方式,其他的定位方式跟这篇总结的主题没关系,不过多讨论。

    其实cssfixed定位是相对浏览器视口,即view point来定位。浏览器视口其实就是浏览器抛掉了操作栏、边框后的窗口区域,如下图红色区域所示:

    viewport示例
    因此我们只需要将背景蒙层利用fixed定位设置为完全覆盖浏览器视口模态窗利用fixed定位将弹窗进行居中定位即可:
    .modal-layer {
        position: fixed;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.5);
        visibility: hidden;
        transform: scale(1.1);
        transition: opacity 0.25s 0s, transform 0.25s;
    }
    .modal-layer.show {
        visibility: visible;
        transform: scale(1.0);
        transition: opacity 0.25s 0s, transform 0.25s;
    }
    .modal-dialog-container {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: white;
        border-radius: 0.5rem;
    }
    

    注意我们是通过show这个className来控制模态窗以及背景蒙层的展示与隐藏。

    实现

    由于我们是基于原生js来实现模态窗,因此我们最好将弹窗封装称为一套工具函数:

    1. init,用来初始化模态窗
    2. destroy,用来在页面退出时销毁模态窗
    3. popup,弹出模态窗
    4. close,关闭模态窗
    function importFromBelow() {
        /**
         * 
         * @param {object} options init options
         * @param {string} options.id dialog id
         * @param {string} options.contentHtml dialog content html
         * @param {boolean} options.closeWhenClickLayer whether close dialog when click layer
         */
        function init(options) {
            const {id: dialogId, contentHtml, closeWhenClickLayer = true} = options;
            const layerEle = document.createElement('div');
            layerEle.setAttribute('class', 'modal-layer');
            layerEle.setAttribute('id', dialogId);
            const contentContainerEle = document.createElement('div');
            contentContainerEle.setAttribute('class', 'modal-dialog-container');
            contentContainerEle.innerHTML = contentHtml;
            layerEle.appendChild(contentContainerEle);
            document.body.appendChild(layerEle);
            if (closeWhenClickLayer) {
                document.onclick = function (e) {
                    if (e.target === layerEle) {
                        close(dialogId);
                    }
                }
            }
        }
    
        /**
         * destroy modal dialog
         * @param {string} dialogId dialog id
         */
        function destroy(dialogId) {
            const dialogEle = document.querySelector(`#${dialogId}`);
            dialogEle.parentNode.removeChild(dialogEle);
        }
    
        function popup(dialogId) {
            const dialogEle = document.querySelector(`#${dialogId}`);
            dialogEle.classList.add('show');
        }
    
        function close(dialogId) {
            const dialogEle = document.querySelector(`#${dialogId}`);
            dialogEle.classList.remove('show');
        }
    
        return {init, destroy, popup, close};
    }
    

    实现完成这套模态窗工具函数后我们可以这样使用:

    const dialogUtil = importFromBelow();
    const DIALOG_ID = 'my-modal-dialog';
    const contentHtml = `
      <div class="modal-dialog-content" id="modal-dialog-content">
        <div>这里是弹窗内容</div>
        <div>
          <button id="dialog-close-button">close modal dialog</button>
        </div>
      </div
    `;
    dialogUtil.init({
      id: DIALOG_ID,
      contentHtml,
      // closeWhenClickLayer: false
    });
    const closeButtonEle = document.querySelector('#dialog-close-button');
    closeButtonEle.onclick = function (e) {
      dialogUtil.close(DIALOG_ID);
    };
    const triggerButton = document.querySelector('#trigger-button');
    triggerButton.onclick = function (e) {
      dialogUtil.popup(DIALOG_ID);
    };
    

    现在让我们看下效果:

    模态窗运行效果
    完整的实现代码见这里

    相关文章

      网友评论

        本文标题:原生js实现模态窗🍀

        本文链接:https://www.haomeiwen.com/subject/lvohhqtx.html