前言:
此时我们对“js中的pop方法”大概比较注重,大家都需要剖析一些“js中的pop方法”的相关内容。那么小编也在网摘上汇集了一些关于“js中的pop方法””的相关资讯,希望我们能喜欢,兄弟们一起来了解一下吧!本文来自我的小伙伴晨荒,全网首发
场景:平常移动端项目中,总是会遇到一些对话框,作用安卓端用户,我希望通过返回键能直接关闭对话框,但是不要退出当前页面,更贴近原生APP的操作方式
对此,笔者发现,通过h5的historyState可以解决此类问题。
在进行操作的时候,我们在history里面推入一条记录,进行返回的时候,我们可以通过window.onpoostate事件来监控,关闭对话框。
借用elementUI的popupjs管理自己的弹窗
element源码 src/utils/popup 文件夹下存放了element的弹窗管理器
用过elementUI的同学不难发现,框架下的每一个对话框,都有一个z-index,且实时变化,每一个对话框都是后来居上
下面我们梳理一下这个管理器
//popup-manager.js(有删减)//作用就是,把遮罩层抽离出各个组件,做一个统一管理let hasModal = false;let hasInitZIndex = false;let zIndex;const instances = {};const PopupManager = { modalFade: true, //遮罩层动画 //获取当前实例 getInstance: function(id) { return instances[id]; }, //注册实例,每个弹窗组件beforeCreate事件中,会生成一个popId ,并调用此方法 register: function(id, instance) { if (id && instance) { instances[id] = instance; } }, //注销实例 deregister: function(id) { if (id) { instances[id] = null; delete instances[id]; } }, //获取最新的Z-index值,每次创建新组件或者弹窗展现时会获取并使用 nextZIndex: function() { return PopupManager.zIndex++; }, //每个实例 如果有配置灰色遮罩层,会在此储存一条记录信息 modalStack: [], //遮罩层点击事件 doOnModalClick: function() { const topItem = PopupManager.modalStack[PopupManager.modalStack.length - 1]; if (!topItem) return; const instance = PopupManager.getInstance(topItem.id); if (instance && instance.closeOnClickModal) { instance.close(); } }, // 打开或创建遮罩层 openModal: function(id, zIndex, dom, modalClass, modalFade) { if (!id || zIndex === undefined) return; this.modalFade = modalFade; const modalStack = this.modalStack; // 查询对应popip的遮罩层是否已经显示 for (let i = 0, j = modalStack.length; i < j; i++) { const item = modalStack[i]; if (item.id === id) { return; } } // 获取当前遮罩层,没有就创建 方法如下 const modalDom = getModal(); //下面是给遮罩层添加类名 addClass(modalDom, 'v-modal'); //添加动画 if (this.modalFade && !hasModal) { addClass(modalDom, 'v-modal-enter'); } if (modalClass) { let classArr = modalClass.trim().split(/\s+/); classArr.forEach(item => addClass(modalDom, item)); } //动画结束 setTimeout(() => { removeClass(modalDom, 'v-modal-enter'); }, 200); //如果方法有传dom,则添加到dom盒子中,不然则添加到body(默认) if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) { dom.parentNode.appendChild(modalDom); } else { document.body.appendChild(modalDom); } if (zIndex) { modalDom.style.zIndex = zIndex; } modalDom.tabIndex = 0; modalDom.style.display = ''; this.modalStack.push({ id: id, zIndex: zIndex, modalClass: modalClass }); }, //关闭遮罩层 closeModal: function(id) { const modalStack = this.modalStack; const modalDom = getModal(); if (modalStack.length > 0) { const topItem = modalStack[modalStack.length - 1]; if (topItem.id === id) { if (topItem.modalClass) { let classArr = topItem.modalClass.trim().split(/\s+/); classArr.forEach(item => removeClass(modalDom, item)); } modalStack.pop(); //如果还存在其他实例遮罩层,这修改zIndex为上一层的层级 if (modalStack.length > 0) { modalDom.style.zIndex = modalStack[modalStack.length - 1].zIndex; } } else { for (let i = modalStack.length - 1; i >= 0; i--) { if (modalStack[i].id === id) { modalStack.splice(i, 1); break; } } } } // 所以遮罩层在modalStack(记录)弹出后,设置遮罩层淡出动画,并讲display设为nonde if (modalStack.length === 0) { if (this.modalFade) { addClass(modalDom, 'v-modal-leave'); } setTimeout(() => { if (modalStack.length === 0) { if (modalDom.parentNode) modalDom.parentNode.removeChild(modalDom); modalDom.style.display = 'none'; PopupManager.modalDom = undefined; } removeClass(modalDom, 'v-modal-leave'); }, 200); } }, /* --------------------控制返回键添加的代码--------------------------------*/ //思路是 执行动作时添加一条记录 使用pushstate //当记录大于一,添加记录时,改为replacestate来管理 ,目的是为了保持路由干净 historyStack:[], pushOrReplaceState:function(id){ let instance = this.getInstance(id); if(!instance)return ; if (this.historyStack.length == 0) { this.historyStack.push(this.getInstance(id)) history.pushState({ id:id }, '', ''); } else { this.historyStack.push(this.getInstance(id)) history.replaceState({ id: id }, '', ''); } }, popState:function(id){ const historyStack = this.historyStack for (let i = 0, j = historyStack.length; i < j; i++) { const item = historyStack[i]; if (item._popupId === id) { history.back(); this.historyStack.splice(i,1); } } } /* ----------------------------------------------------*/};//获取一个遮罩层const getModal = function() { // 如果管理器存在一个遮罩层 便直接返回 let modalDom = PopupManager.modalDom; if (modalDom) { hasModal = true; } else { hasModal = false; //创建一个遮罩层 , 并为遮罩层添加事件 modalDom = document.createElement('div'); PopupManager.modalDom = modalDom; modalDom.addEventListener('touchmove', function(event) { event.preventDefault(); event.stopPropagation(); }); //遮罩层点击事件 modalDom.addEventListener('click', function() { PopupManager.doOnModalClick && PopupManager.doOnModalClick(); }); } return modalDom;};
popup-manager相当于一个中枢,每一个弹出层需要一个遮罩层,告诉我你的ID,然后openModel就可以了
至于你想知道最顶层的zIndex应该是多少,调用popupManaer.nextZIndex()便可获取
借用它,我们不用在每个对话框都添加一个遮罩层。 简单的,我们只要写设置好定位,然后通过nextZindex获取最顶层的值并设置就可以了
下面怎么开发我们的组件呢?
有的同学应该发现了,popup文件夹下还有一个index.js 。它是基于popup-manager.js,在组件各个生命周期进行操作的mixins对象
下面看部分代码(这里不贴源码了,建议读者下载源码查看)
// 这个是用于生成ID的参数,每个minxin混入的组件,都可以通过闭包拿到它便使用它let idSeed = 1;{ ..., beforeMount() { //每次组件创前,都生产一个唯一的IP告诉popup-manager this._popupId = 'popup-' + idSeed++; PopupManager.register(this._popupId, this); }, beforeDestroy() { //组件注销 PopupManager.deregister(this._popupId); PopupManager.closeModal(this._popupId); }, watch:{ visible(){... //visible 作为props值,由业务层提供 //为true时 进行open操作 //为false时 进行close操作 } , }, methods:{ open(options){... //被调用的地方是 watch:visible //整合参数传入的配置,相当于打开遮罩前函数(options提供钩子能力) // 业务上主要做了一个延迟打开的逻辑 }, doOpen(){... //主要用于打开遮罩(组件modal为true的时候) //给对话框加上最新的zIndex //调用onOpen钩子函数 }, doAfterOpen(){... //修改_opening为false // !!笔者在这添加了history状态代码 //如果组件需要通过路由改变状态 if(this.historyControl){ PopupManager.pushOrReplaceState(this._popupId) } }, close(){... //如果组件需要通过路由改变状态 if(this.historyControl){ PopupManager.popState(this._popupId) } //此方法主要是关闭组件的设置方法 // 同上面open方法一样 添加了延迟逻辑 }, doClose(){... //调用了onClose钩子函数 供业务处理 }, doAfterClose(){... // 主要是关闭遮罩层 }, }}
这个js文件很简单,它为组件提供了适用popup-manager的方法,巧妙的设置钩子,让我们不用去理会时候打开或关闭遮罩层,调用设置的钩子我们就可以完成组件业务。
下期,我们将使用上面的工具方法实现一个仿VUX的对话框。。未完待续。。。谢谢您的关注
标签: #js中的pop方法