重寫IE的showModalDialog模態框以兼容現代瀏覽器
背景
之前有個項目是 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();
};
});
});
}

浙公網安備 33010602011771號