重写IE的showModalDialog模态框以兼容现代浏览器
<h4 id="背景">背景</h4><p>之前有个项目是 <code>jsp</code> 的,之前都是在 IE 浏览器上运行,现在要将这个项目做兼容性改造(信创),需要兼容谷歌。所以需要将项目中的公共弹框给改掉,而项目中模态框基本上都是用的 <code>showModalDialog</code>。</p>
<h4 id="介绍-showmodaldialog">介绍 showModalDialog</h4>
<p><code>showModalDialog</code> 是微软在早期版本的(IE)中引入的一个方法,用于创建模态对话框。不过在现代浏览器中已经不再支持这个方法了。</p>
<p>用法(参考MDN):</p>
<pre><code>returnVal = window.showModalDialog(uri[, arguments][, options]);
</code></pre>
<ul>
<li><code>returnVal</code> 模态框的返回值。</li>
<li><code>uri</code> 要在模态对话框中打开的页面 URI。</li>
<li><code>arguments</code> 可选变量。可以通过该参数将需要的值传入对话框。</li>
<li><code>options</code> 可选字符串参数。用于设置对话框打开的样式,使用一个或多个逗号分隔。</li>
</ul>
<p>缺点:</p>
<ul>
<li>现代浏览器不支持</li>
<li>模态框没有遮罩层,无法做到处理完模态框再处理父级页面</li>
</ul>
<h4 id="重写">重写</h4>
<h6 id="步骤">步骤</h6>
<ul>
<li>使用 <code>iframe</code> 代替模态框中的内容包裹层(传递给模态框的<code>uri</code>是后台一个接口,接口返回一个<code>jsp</code>页面,模态框展示的就是这个页面,而 <code>iframe</code> 可以用来嵌入内容)</li>
<li>仍然支持 <code>showModalDialog</code> 的三个参数以及返回值给父页面</li>
<li>添加遮罩层,关闭模态框之前不能点击并处理父级页面</li>
<li>重写关闭模态框的方法(之前模态框关闭方法是 <code>window.close</code>)</li>
</ul>
<h5 id="编写函数">编写函数</h5>
<p>1、定义默认样式</p>
<pre><code class="language-js">//定义默认样式
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;";
</code></pre>
<p>2、获取项目顶层的 <code>document</code>,弹框要覆盖所有页面,必须添加到顶层 <code>window</code> 中</p>
<pre><code class="language-js">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) => {
//···
})
}
</code></pre>
<p>这里返回一个 <code>promise</code> ,返回值也会通过 <code>resolve</code> 返回给父级页面,父页面通过 <code>.then</code> 或者 <code>await</code> 接收。</p>
<p>3、创建弹框盒子,弹框<code>header</code>,<code>iframe</code>区域,<code>footer</code>,添加遮罩层</p>
<pre><code class="language-js">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).replace("width:", "")
);
var marginTop = parseFloat(
iframeStyle.match(/height\:(\d)+px;/g).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)
</code></pre>
<p>需要注意的是,上面定义了 <code>iframe.destroy</code> 和 <code>removeStorageClass</code> 方法用来给某些特殊页面用的,那些特殊页面因为表单操作刷新了当前 <code>window</code>,导致调用不到了我们的重写的 <code>close</code> 方法。所以只能那些页面中处理完业务后手动调用 <code>destroy</code> 方法关闭模态框。</p>
<p>4、<code>iframe</code>加载完毕后,重写模态框的关闭方法</p>
<pre><code class="language-js">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();
};
});
</code></pre>
<h5 id="完整代码">完整代码</h5>
<pre><code class="language-js">//定义默认样式
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).replace("width:", "")
);
var marginTop = parseFloat(
iframeStyle.match(/height\:(\d)+px;/g).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();
};
});
});
}
</code></pre><br><br>
来源:https://www.cnblogs.com/zsxblog/p/18919455
頁:
[1]