高版本Chrome兼容window.showModalDialog办法
方式一:回调
兼容方式:
由于showmodaldialog 不属于W3C标准,在新版本浏览器中不再受支持,我们需要使用window.open等方法自定义一个showmodaldialog 代替。
兼容效果:
不和当前使用的前端框架冲突的情况下无需对被打开的模态框子页面做任何修改。
打开模态框的主页面可能需要修改传入参数(如果格式统一可以在showmodaldialog写死,从而不修改参数)。
下面直接上代码,复制即可测试。
将要打开模态框的主页面papa.html代码:
<!DOCTYPE html>
<html lang="zh">
<head>
<title>主页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
</head>
<body>
<input id="a"/>
<button onclick="callSon()">打开模态框</button/>
<fieldset>
<legend>子页面返回</legend>
<span id="content"></span>
</fieldset>
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.js"></script>
<Script type="text/javascript" src="papa.js"></script >
</body>
</html>
将要打开模态框的主页面papa.js代码:
//定义一个全局变量判定是否原生支持showModalDialog方法
var has_showModalDialog = !!window.showModalDialog;
//当其不受支持时自定义window.showModalDialog
if(!has_showModalDialog){
/* url传入被打开网页的地址
* dialogArguments传入子页面需要的参数和判断参数:
* windowName4Open传入被打开网页的名称;
* callback4Open传入子窗口返回returnValue时的回调;
* autoCallback4Open用于打开的网页判断callback4Open是否存在
* features子网页的样式*/
window.showModalDialog = function(url,dialogArguments,features){
//新窗口的名字。如果该名字的窗口已经存在,则再次调用时占用该窗口,不再新建窗口。如果省略,(浏览器会)默认_blank
var name = dialogArguments.windowName4Open || '_blank';
if(name=='_blank' && window.hasOpenWindow){
//当窗口名为_blank时,再次调用不会占用未关闭的_blank子窗,需要手动避免弹出多个窗口
window.myNewWindow.focus();
return;
}
//因window.showmodaldialog 与 window.open 参数不一样,所以封装的时候用正则去格式化一下参数
if(features)
features = "modal=yes,"+features.replace(/(dialog)|(px)/ig,"").replace(/;/g,',').replace(/\:/g,"=");
var left = (window.screen.width - parseInt(features.match(/width[\s]*=[\s]*([\d]+)/i)[1]))>>1;
var top = (window.screen.height - parseInt(features.match(/height[\s]*=[\s]*([\d]+)/i)[1]))>>1;
features+=',left='+left+',top='+top;//窗口居中
//打开子页面
window.myNewWindow = window.open(url,name,features);
//定义自动返回returnValue
if("function" === typeof dialogArguments.callback4Open /*其他条件*/){
//如果不冲突的情况下通过劫持子窗returnValue,达到无需在子窗手动执行回调的目的
dialogArguments.autoCallback4Open=true;
Object.defineProperty(myNewWindow, 'returnValue', {
get: function() {
//不需要写成window.myNewWindow.returnValue就可以
return returnValue;
},
set: function(value) {
returnValue = value;
//执行函数
dialogArguments.callback4Open(value);
}
});
}else{
Object.defineProperty(myNewWindow, 'returnValue', {});
}
window.hasOpenWindow = true;
if(dialogArguments){
//open()不支持传递参数,通过这种方式向子页面传递参数,因为打开页面速度远远慢于本方法执行速度,因而子页面总能获得传递的参数,
//也可在子窗load事件发生后使用myNewWindow.postMessage(dialogArguments,url),但在子窗还未load时监听可能无效,但它可以突破同源限制
window.myNewWindow.dialogArguments=dialogArguments;
}
//window.myNewWindow.moveTo(left,top);
}
}
//打开模态框
function callSon(){
url = 'son.html';
var sonStyle="dialogWidth:500px;dialogHeight:450px;help:no;resizable:no;center:yes;scroll:yes;status:no";
var param={val:document.getElementById("a").value?document.getElementById("a").value:"son"}
//固定情况下这两个可以写死在showModalDialog,如果不想多次点击打开多个子页面,则不要给子页面命名
param.windowName4Open='son';
param.callback4Open=callSonChrome;
var val = window.showModalDialog(url,param,sonStyle);
//chrome下返回不执行afterCall,而是作为回调(callSonChrome),因为IE下showModalDialog是阻塞的可以直接afterCall,open则是异步的;
if(!has_showModalDialog)
return;
afterCall(val);
}
//为打开的子窗口定义方法,让打开的窗口关闭时通过window.opener赋值回来并执行
function callSonChrome(val){
afterCall(val);
}
//获得模态框返回值后执行的业务方法
function afterCall(val){
$("#content").html(val);
}
被打开的模态框子页面son.html代码:
<!DOCTYPE html>
<html lang="zh">
<head>
<title>子页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
</head>
<body>
<input id="a"/>
<button onclick="closeToRetuen()">关闭模态框并返回</button>
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.js"></script>
<Script type="text/javascript" src="son.js"></script >
</body>
</html>
被打开的模态框子页面son.js代码:
window.addEventListener('message', function (e) {
//监听父窗postMessage发送的消息
console.log(e.data);
},false);
var param = window.dialogArguments;
document.getElementById("a").value=param.val;
function closeToRetuen() {
var a = $("#a").val();
//chrome环境未劫持returnValue情况
if (window.opener != undefined && param.callback4Open && !param.autoCallback4Open ) {
//关闭前调用父窗口方法返回需要返回的对象或字符串
param.callback4Open(a);
}
//ie环境和chrome环境劫持returnValue情况
else {
window.returnValue = a;
}
window.close();
}
//_blank情况下页面关闭时主动通知调用页面我将关闭
window.onbeforeunload=function(){
window.opener.hasOpenWindow=false;
}
测试:
测试浏览器Google Chrome 版本 69.0.3497.100(正式版本) (64 位)
打开模态框
关闭模态框
参考文章:
https://blog.csdn.net/ts472960087/article/details/45843257
方式二:postMessage
兼容方式
较新的浏览器现在基本上都支持window.postMessage方法,我们可以通过他来进行不同页面之间的跨域通信(注意!这有跨域安全问题!)
兼容效果
代码改动量较少,子页面执行postMessage,主页面仅需添加监听即可,但需要较高的浏览器版本,不同浏览器之间有细微差异(基本功能不影响)
语法
otherWindow.postMessage(message, targetOrigin, [transfer]);
/*
otherWindow
其他窗口的一个引用,
比如iframe的contentWindow属性、
执行window.open返回的窗口对象、
或者是命名过或数值索引的window.frames。
message
将要发送到其他 window的数据。
它将会被结构化克隆算法序列化。
这意味着你可以不受什么限制的将数据对象安全的传送给目标窗口而无需自己序列化。
targetOrigin
通过窗口的origin属性来指定哪些窗口能接收到消息事件,
其值可以是字符串"*"(表示无限制)或者一个URI。
在发送消息的时候,
如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;
只有三者完全匹配,消息才会被发送。
这个机制用来控制消息可以发送到哪些窗口;例如,当用postMessage传送密码时,这个参数就显得尤为重要,
必须保证它的值与这条包含密码的信息的预期接受者的origin属性完全一致,来防止密码被恶意的第三方截获。
如果你明确的知道消息应该发送到哪个窗口,那么请始终提供一个有确切值的targetOrigin,而不是*。
不提供确切的目标将导致数据泄露到任何对数据感兴趣的恶意站点。
transfer (可选)
是一串和message 同时传递的 Transferable 对象.
这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
*/
//简单示例
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
// For Chrome, the origin property is in the event.originalEvent
// object.
// 这里不准确,chrome没有这个属性
// var origin = event.origin || event.originalEvent.origin;
var origin = event.origin
if (origin !== "http://example.org:8080")
return;
// ...
}
/*
message 的属性有:
data
从其他 window 中传递过来的对象。
origin
调用 postMessage 时消息发送方窗口的 origin . 这个字符串由 协议、“://“、域名、“ : 端口号”拼接而成。
例如 “https://example.org (隐含端口 443)”、“http://example.net (隐含端口 80)”、“http://example.com:8080”。
请注意,这个origin不能保证是该窗口的当前或未来origin,因为postMessage被调用后可能被导航到不同的位置。
source
对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。
/*
下面直接上代码.
将要打开模态框的主页面js代码:
/*
* A窗口的域名是<http://example.com:8080>,以下是A窗口的script标签下的代码:
*/
var popup = window.open(...popup details...);
// 如果弹出框没有被阻止且加载完成
// 这行语句没有发送信息出去,即使假设当前页面没有改变location(因为targetOrigin设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
"https://secure.example.net");
// 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)
popup.postMessage("hello there!", "http://example.org");
function receiveMessage(event)
{
// 我们能相信信息的发送者吗? (也许这个发送者和我们最初打开的不是同一个页面).
if (event.origin !== "http://example.org")
return;
// event.source 是我们通过window.open打开的弹出页面 popup
// event.data 是 popup发送给当前页面的消息 "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);
被打开的模态框子页面js代码:
/*
* 弹出页 popup 域名是<http://example.org>,以下是script标签中的代码:
*/
//当A页面postMessage被调用后,这个function被addEventListener调用
function receiveMessage(event)
{
// 我们能信任信息来源吗?
if (event.origin !== "http://example.com:8080")
return;
// event.source 就当前弹出页的来源页面
// event.data 是 "hello there!"
// 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把event.source
// 作为回信的对象,并且把event.origin作为targetOrigin
event.source.postMessage("hi there yourself! the secret response " +
"is: rheeeeet!",
event.origin);
}
window.addEventListener("message", receiveMessage, false);
参考文章:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage