easyUi内嵌iframe时,弹出对话框被遮挡问题

这是多年以前用传统JQuery(easyUi)做项目时遇到的问题,今天把解决思路发布出来,也许能帮助到还在使用老架构的同学。

在开发类ERP的后台管理系统时,经常会看到这样的布局结构


每打开一个菜单都是单独打开一个Tab页,easyUi打开Tab页有两种方式:

动态增加iframe在同一个Dom树中增加div 的方式

同一Dom树中存在命名污染的问题,包括样式也会互相影响,新增加页面里的样式很可能影响原有页面的布局

因此我们采用iframe的方式实现:

<code>function addTab(subtitle,url,icon,closable){if(!$('#tabs').tabs('exists',subtitle)){$('#tabs').tabs('add',{title:subtitle,content:createFrame(url),closable:(closable==null?true:closable),icon:icon,fit:true});}else{$.messager.confirm('提示',eship.messages.updateTab,function(r){ if (r){ $('#tabs').tabs('select',subtitle);var currTab = $('#tabs').tabs('getSelected');$('#tabs').tabs('update',{tab:currTab,options:{content:createFrame(url)}});}});}tabClose();}function createFrame(url){var s = '<iframe>';return s;}/<code>

我们在使用时iframe Tab页时,页面中经常会有打开对话框的需求。

因为我们采用了iframe的方式,其实打开对话框是在子页面的iframe的dom中打开的,因此会被主页面遮挡,如图所示:


对话框移动过程中会被Tab标签遮挡,并且无法继续向上移动,并且不同iframe传递参数也比较麻烦,不是很方便,因此决定进行封装,基本原理如下:

封装模态对话框插件

判断是否在最顶层dom中执行,如果不在获取到顶层dom对象在最顶层iframe中自动增加一个div,并通过这个div打开模态对话框用堆栈记录打开对话框的相关属性options关闭时,依次调用堆栈中的对话框,并关闭

优点:

增加getDialogOption方法,可方便获取当前对话框的option对象,可以方便的通过该对象进行窗口之间的值传递。在关闭时,增加回调方法,可以在关闭时方便的调用父窗体的回调方法。

<code>/** * jQuery dialog * * @author rogerkuo * * easy ui 对话框扩展,自动创建DIV,并将DIV放置在最顶层DOM中,避免对话框被iframe覆盖的情况 */(function($) {$.joyplus = function(){};$.joyplus.messager = function(){};//私有方法,用来负责创建对话框function _joy_show(options) {var defaults = {width : 800,height : 500,title : 'My Dialog',modal : true,onLoad : function(){var topDiaOptions = $.joyplus.getDialogOption();if( topDiaOptions.dialogId != undefined){//如果需要处理对话框连续弹出问题时,对焦点进行处理var topBody = _joy_get_top_body();var ele = $(topBody).find("#" + $.joyplus.getModelDialogId());var inputs = $(ele).find("input");if( inputs.length > 0 ){inputs[0].focus();}}}//dialogId : ""用来判断是否同一窗体,避免同一个业务多次弹出一个对话框,造成对话框堆叠,如果没有,不作处理};var isString = (typeof options == "string");if (isString) {options = {};}var d = {};$.extend(d , options);var topDiaOptions = $.joyplus.getDialogOption();if( topDiaOptions != undefined && topDiaOptions.dialogId != undefined && topDiaOptions.dialogId == d.dialogId ){return;}var isDataFunction = false;//新增dialog传值基础功能,可调用父窗体的方法,获取附加数据if( jQuery.type(d.extdata) == "function"){isDataFunction = true;d.extdata = d.extdata.call(this);}//新增dialog传值grid功能if( jQuery.type(d.grid) == "function"){isDataFunction = true;d.grid = d.grid.call(this);}if(!isDataFunction){d = $.extend(d , options);}_joy_get_options_cache().push(d);var opt = $.extend(defaults, options);opt.onClose = $.joyplus.closeModelDialog;var id = _joy_create_div();var top = _joy_get_top_body();var element = $(top).find("#" + id);$(element).dialog(opt);};//获取最上层的dialog对象function _joy_get_top_dialog() {var stack = _joy_get_dialog_stack();return stack.pop();};//获取对话框堆栈,因为用户可以在对话框上继续点开对话框,因次要记录对话框堆栈//先进后出function _joy_get_dialog_stack() {var top = _joy_get_cahce();return top;};//自动创建div,在dom的最顶层创建function _joy_create_div() {var new_id = _joy_get_new_id();var stack = _joy_get_dialog_stack();stack.push(new_id);$(_joy_get_top_body()).last().append("

");return new_id;};function _joy_get_top_body() {var top = window.parent.document.body;return $(top)};function _joy_get_cahce() {var a = window.top.$.fn._cache;return a;};function _joy_get_options_cache() {return window.top.$.fn._options_cache;};function _joy_get_new_id() {var date = new Date();return date.getTime().toString();};$.joyplus.showModelDialog = function(options , ifr) {if( ifr ){_joy_show(options);}else{//如果是最顶层的iframeif (window.top == window) {_joy_show(options);} else {//如果不是最顶层的iframe,则执行最顶层的showModelDialog方法options.curDom = window;window.top.$.joyplus.showModelDialog(options);}}};//获取当前对话框的options$.joyplus.getDialogOption = function(){var options = _joy_get_options_cache();return options[options.length-1];}; //延时关闭$.joyplus.delayedClose = function(millisecond){var dialogId = $.joyplus.getModelDialogId();if(dialogId){$('#'+dialogId).dialog('minimize');}setTimeout($.joyplus.closeModelDialog,millisecond);}; $.joyplus.closeModelDialog = function() {if (window.top == window) {var options = _joy_get_options_cache().pop(); //父窗体可以实现onClose方法,在对话框关闭时,会调用该方法if (options.onClose != undefined && $.isFunction(options.onClose)) {options.onClose();} //提供父窗体回调函数if (options.callBack != undefined && $.isFunction(options.callBack)) {options.callBack();} if (options.validObj != undefined) {options.validObj.validatebox("validate");} //获取当前对话框idvar id = _joy_get_top_dialog();var top = _joy_get_top_body(); //释放自动创建的divvar element = $(top).find("#" + id);$(element).dialog("destroy");} else {window.top.$.joyplus.closeModelDialog();}};$.joyplus.messager.alert = function(title, msg, icon, fn){if( eship.messages[msg] != undefined ){msg = eship.messages[msg];}$.messager.alert(title, msg, icon, fn);};//获取dialog对象ID$.joyplus.getModelDialogId = function() {var c = _joy_get_cahce();return c[c.length - 1];};$.fn._cache = new Array();$.fn._options_cache = new Array();})(jQuery);/<code>

这样在使用时,只需调用简单方法即可

<code>function invCageEdit(){var row = $('#invCageDG').datagrid('getSelected');if(row){ var options = {title:"出库包装",height:500,width:1000,contentOverflow:'hidden'}; options.invCageId = row.uuid;//需要传递的扩展属性 options.href = contextPath +'/web/comm/invCageItemDialog.jsp'; options.backFunc = function(data){ //updatePickItem();回调函数 }; $.joyplus.showModelDialog(options);}else{$.joyplus.messager.alert('提示',eship.messages.selectOne,'info');}}/<code>