背景
之前有个项目是 jsp 的,之前都是在 IE 浏览器上运行,现在要将这个项目做兼容性改造(信创),需要兼容谷歌。所以需要将项目中的公共弹框给改掉,而项目中模态框基本上都是用的 showModalDialog。
介绍 showModalDialog
showModalDialog 是微软在早期版本的(IE)中引入的一个方法,用于创建模态对话框。不过在现代浏览器中已经不再支持这个方法了。
用法(参考MDN):
returnVal = window.showModalDialog(uri[, arguments][, options]);
returnVal模态框的返回值。uri要在模态对话框中打开的页面 URI。arguments可选变量。可以通过该参数将需要的值传入对话框。options可选字符串参数。用于设置对话框打开的样式,使用一个或多个逗号分隔。
缺点:
- 现代浏览器不支持
- 模态框没有遮罩层,无法做到处理完模态框再处理父级页面
重写
步骤
- 使用
iframe代替模态框中的内容包裹层(传递给模态框的uri是后台一个接口,接口返回一个jsp页面,模态框展示的就是这个页面,而iframe可以用来嵌入内容) - 仍然支持
showModalDialog的三个参数以及返回值给父页面 - 添加遮罩层,关闭模态框之前不能点击并处理父级页面
- 重写关闭模态框的方法(之前模态框关闭方法是
window.close)
编写函数
1、定义默认样式
//定义默认样式 var DIALOG_CLASS = "modernShowDialog"; //弹框类名 var SUB_STYLE = "position:absolute;top:50%;left:50%;margin-1eft:-50%;margin-top:150px;background:#fff;border-radius: 4px;"; var IFAME_STYLE = "width:100%;border: none;height: 70%;"; var dialog_header_style = "width:100%;height:32px;font-size:14px;line-height:34px;";
2、获取项目顶层的 document,弹框要覆盖所有页面,必须添加到顶层 window 中
function showModalDialog(pageUrl, comeInParams, iframeStyle) { //获取传进来的宽高 iframeStyle = iframeStyle.replace(/dialogwidth/g, "width"); iframeStyle = iframeStyle.replace(/dialogHeight/g, "height"); //获取项目顶层的 document var topDoc = window.top.document; var topDocBody = window.top.document.body; var mainFrameSet = topDoc.querySelector("#setMain"); // 最终生成的模态框会插入到 mainFrameSetParent 中 var mainFrameSetParent = mainFrameSet.parentNode; return new Promise((resolve, reject) => { //··· }) }
这里返回一个 promise ,返回值也会通过 resolve 返回给父级页面,父页面通过 .then 或者 await 接收。
3、创建弹框盒子,弹框header,iframe区域,footer,添加遮罩层
return new Promise((resolve, reject) => { //创建弹框盒子,添加遮罩层 var dialog = document.createElement("div"); dialog.className = DIALOG_CLASS + Date.now(); dialog.style = DIALOG_STYLE + ";width:" + topDocBody.clientwidth + "px;"; //创建弹框里面用来包裹iframe的 div var dialogBody = document.createElement("div"); dialogBody.className = "dialogBody"; var marginLeft = parseFloat( iframeStyle.match(/width:(d)+px;/g)[0].replace("width:", "") ); var marginTop = parseFloat( iframeStyle.match(/height:(d)+px;/g)[0].replace("height:", "") ); dialogBody.style = SUB_STYLE + ";margin-left:-" + marginLeft / 2 + "px;margin-top:-" + marginTop / 2 + "px;"; //创建 header var header = document.createElement("div"); header.className = "dialog_header"; header.style = dialog_header_style; var headerBtn = document.createElement("div"); headerBtn.style = "cursor:pointer;text-align:right;padding-right:10px;font-size: 20px;user-select: none;"; headerBtn.textContent = "x"; headerBtn.onclick = function () { mainFrameSetParent.removeChild(dialog); }; header.appendChild(headerBtn); //创建 iframe 包裹层 var iframe = document.createElement("iframe"); iframe.src = pageUrl; iframe.className = "modernDialogIframe"; iframe.style = IFAME_STYLE + iframeStyle; iframe.destroy = function (returnValue) { resolve(returnValue); mainFrameSetParent.removeChild(dialog); removeStorageClass(); }; dialogBody.appendChild(header); dialogBody.appendChild(iframe); dialog.appendChild(dialogBody); var removeStorageClass = function () { var existClass = sessionStorage.getItem("dialogClass"); if (existClass) { sessionStorage.setItem( "dialogClass", existClass.split(",").pop().join(",") ); } }; //将创建好的弹框插入顶层 document mainFrameSetParent.appendChild(dialog); // 通过sessionStorage 存储当前显示的弹框的类名, //给某些特定页面(通过windbw.close 关闭不了的页面,因为里面的window可能表单操作刷新了window)使用 var session = sessionStorage.getItem('dialogClass') sessionStorage.setItem('dialogClass',session ? (session + ',' + dialog.className) : dialog.className)
需要注意的是,上面定义了 iframe.destroy 和 removeStorageClass 方法用来给某些特殊页面用的,那些特殊页面因为表单操作刷新了当前 window,导致调用不到了我们的重写的 close 方法。所以只能那些页面中处理完业务后手动调用 destroy 方法关闭模态框。
4、iframe加载完毕后,重写模态框的关闭方法
var modernDialogIframe = topDoc.querySelector( "." + dialog.className + " .modernDialogIframe" ); var tempValue = null; //监听 iframe 包裹的目标页面的加载情况 modernDialogIframe.contentwindow.addEventListener("load", function () { this.dialogArguments = comeInParams; this.oldClose = this.close; //重写当前页面的 window.close this.close = function () { // returnValue 是业务页面中定义的全局变量 tempValue = this.returnValue; setTimeout(function () { resolve(tempValue); }, 10); mainFrameSetParent.removeChild(dialog); removeStorageClass(); this.oldClose(); }; });
完整代码
//定义默认样式 var DIALOG_CLASS = "modernShowDialog"; var SUB_STYLE = "position:absolute;top:50%;left:50%;margin-1eft:-50%;margin-top:150px;background:#fff;border-radius: 4px;"; var IFAME_STYLE = "width:100%;border: none;height: 70%;"; var dialog_header_style = "width:100%;height:32px;font-size:14px;line-height:34px;"; /** * 模拟 IE 的 showModalDialog 方法,同样接收三个参数; * pageUrl 表示要显示的页面地址 * comeInParams 表示传进目标页面的参数 * iframeStyle 表示自定义页面样式,比如宽高 **/ function showModalDialog(pageUrl, comeInParams, iframeStyle) { iframeStyle = iframeStyle.replace(/dialogwidth/g, "width"); iframeStyle = iframeStyle.replace(/dialogHeight/g, "height"); //获取项目顶层的 document,弹框显示需要覆盖顶层页面 var topDoc = window.top.document; var mainFrameSet = topDoc.querySelector("#setMain"); var mainFrameSetParent = mainFrameSet.parentNode; var topDocBody = window.top.document.body; return new Promise((resolve, reject) => { //创建弹框盒子,添加遮罩层 var dialog = document.createElement("div"); dialog.className = DIALOG_CLASS + Date.now(); dialog.style = DIALOG_STYLE + ";width:" + topDocBody.clientwidth + "px;"; //创建弹框里面用来包裹iframe的 div var dialogBody = document.createElement("div"); dialogBody.className = "dialogBody"; var marginLeft = parseFloat( iframeStyle.match(/width:(d)+px;/g)[0].replace("width:", "") ); var marginTop = parseFloat( iframeStyle.match(/height:(d)+px;/g)[0].replace("height:", "") ); dialogBody.style = SUB_STYLE + ";margin-left:-" + marginLeft / 2 + "px;margin-top:-" + marginTop / 2 + "px;"; //创建 header var header = document.createElement("div"); header.className = "dialog_header"; header.style = dialog_header_style; var headerBtn = document.createElement("div"); headerBtn.style = "cursor:pointer;text-align:right;padding-right:10px;font-size: 20px;user-select: none;"; headerBtn.textContent = "x"; headerBtn.onclick = function () { mainFrameSetParent.removeChild(dialog); }; header.appendChild(headerBtn); //创建 iframe 包裹层 var iframe = document.createElement("iframe"); iframe.src = pageUrl; iframe.className = "modernDialogIframe"; iframe.style = IFAME_STYLE + iframeStyle; iframe.destroy = function (returnValue) { resolve(returnValue); mainFrameSetParent.removeChild(dialog); removeStorageClass(); }; dialogBody.appendChild(header); dialogBody.appendChild(iframe); dialog.appendChild(dialogBody); var removeStorageClass = function () { var existClass = sessionStorage.getItem("dialogClass"); if (existClass) { sessionStorage.setItem( "dialogClass", existClass.split(",").pop().join(",") ); } }; //将创建好的弹框插入顶层 document mainFrameSetParent.appendChild(dialog); // 通过sessionStorage 存储当前显示的弹框的类名,给某些特定页面(通过windbw.close 关闭不了的页面,因为里面的window可能表单操作刷新了window)使用 var session = sessionStorage.getItem('dialogClass') sessionStorage.setItem('dialogClass',session ? (session + ',' + dialog.className) : dialog.className) var modernDialogIframe = topDoc.querySelector( "." + dialog.className + " .modernDialogIframe" ); var tempValue = null; //监听 iframe 包裹的目标页面的加载情况 modernDialogIframe.contentwindow.addEventListener("load", function () { this.dialogArguments = comeInParams; this.oldClose = this.close; //重写当前页面的 window.close this.close = function () { tempValue = this.returnValue; setTimeout(function () { resolve(tempValue); }, 10); mainFrameSetParent.removeChild(dialog); removeStorageClass(); this.oldClose(); }; }); }); }