谷歌浏览器Chrome不再支持showModalDialog的解决办法


2014年的某一天,chrome升级后,突然发现某个在用的系统不能弹出模态窗口了,查找各种资料后确认新版本(可能为Chrome 37+)确实把这个支持去掉了,有这么坑人的吗!?为避免大家少走弯路,特发布此文提供确认躺枪和解决办法。

问题重现

弹出窗口编码:

JavaScript

var obj = new Object(); 
var retval = window.showModalDialog("request.aspx",obj,"dialogWidth=500px;dialogHeight=300px");
if (retval == null) {
...
}else {
...
}
var obj = new Object ( ) ;  

var retval = window . showModalDialog ( "request.aspx" , obj , "dialogWidth=500px;dialogHeight=300px" ) ;

if ( retval == null ) {

. . .

} else {

. . .

}

浏览器异常:

Shell

Uncaught TypeError: undefined is not a function
Uncaught TypeError : undefined is not a function

如果出现这个异常,很不幸你已经躺枪了。关于这个问题可以看这里:

http://windowsitpro.com/blog/google-kills-showmodaldialog-api-chrome-37-and-does-evil-exchange-owa

http://www.infoq.com/news/2014/09/chrome-showmodaldialog

其中有些临时解决办法,但貌似showModalDialog不会回来了。

问题解决

常见的弹出窗口有div模拟或者用window.open代替,对于一个已经在用的系统来说,采用div方式转换成本较高,采用window.open改动会更少一些,但也会丢失其模态性。

这里采用简单的window.open方案,毕竟替换成本低很多。针对上文中提到的showModalDialog使用方式,替换为:

JavaScript

var iWidth = 500;
var iHeight = 300;
var iTop = (window.screen.availHeight - 30 - iHeight) / 2;
var iLeft = (window.screen.availWidth - 10 - iWidth) / 2;
var win = window.open("request.aspx", "弹出窗口", "width=" + iWidth + ", height=" + iHeight + ",top=" + iTop + ",left=" + iLeft + ",toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no,alwaysRaised=yes,depended=yes");
var iWidth = 500 ;

var iHeight = 300 ;

var iTop = ( window . screen . availHeight - 30 - iHeight ) / 2 ;

var iLeft = ( window . screen . availWidth - 10 - iWidth ) / 2 ;

var win = window . open ( "request.aspx" , "弹出窗口" , "width=" + iWidth + ", height=" + iHeight + ",top=" + iTop + ",left=" + iLeft + ",toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no,alwaysRaised=yes,depended=yes" ) ;

采用这种方式就可以打开一个和之前使用showModalDialog差不多的窗口。但是怎么返回值呢?

在弹出页面中有两种方式:

1、直接设置父窗口的DOM对象的值。

JavaScript

window.opener.document.getElementById(“parentWindowControlId”).value = "数据";
window . opener . document . getElementById ( “ parentWindowControlId ” ) . value = "数据" ;

父窗口中应该有一个id为parentWindowControllId的DOM元素。

2、调用父窗口中的Javascript函数,由父窗口进行相应的处理。

JavaScript

var obj = {
id:"id",
name:"name"
};
window.opener.DoAfterXXX(obj);
var obj = {

id : "id" ,

name : "name"

} ;

window . opener . DoAfterXXX ( obj ) ;

父窗口提供一个DoAfterXXX的函数就可以了。

长远来看window.open由于其用户体验问题必将走向没落,大家还是尽快转移为好。


昨天在使用showModalDialog的时候,遇到如下问题:
如果子窗口被刷新过,那么父窗口就接受不到子窗口的返回值。

为了解决这个问题,哎,我纠结了好久,最后才发现不是代码的问题,而是多浏览器兼容问题,因为我是用Chrome调试的,而Chrome是不支持showModalDialog的,所以父窗口才没有收到子窗口的返回值的,在IE下就没有这个问题了。

后来在网上查了一下,原来在chrome下用showModalDialog打开的并不是模态窗口,而是和打开一个普通页面一样,父窗口还是可以获取焦点,执行相关操作。所以父窗口用var returnValue=window.showModalDialog(url[,vArguments][,sFeatures])接收子窗口的返回值时,这个returnValue总是undefined。

因为在chrome下,即使是用window.showmodalDialog()打开一个新窗口,那也还是和window.open()一样,我们可以在子窗口通过如下js脚本验证一下window.opener是否为空
<script type="text/javascript">
        alert(window.opener);
</script>
通过调试,可以发现,在chrome中,显示的是一个[object window]对象,而IE则是undefined。现在知道原来chrome将showModalDialog当作window.open来处理了。也就是说如果我们想在chrome下给父窗口返回值,不能直接用window.returnValue,而是应该用window.opener.returnValue。

所以,在用showModalDialog时,并且父窗口和子窗口之间存在传值时,如果想兼顾IE和chrome,可以做如下处理:
父窗口js脚本:
var returnValue = window.showModalDialog("son.html ", window,"dialogWidth:400px;dialogHeight:400px");
//for chrome
if (!returnValue) {
    returnValue = window.returnValue;
}
子窗口js脚本:
if (window.opener) {
       //for chrome
       window.opener.returnValue = "opener returnValue";
}
else {
       window.returnValue = "window returnValue";
}
window.close();//关闭子窗口
       PS:以上的代码环境是IE 8.0.7601.17514和Chrome 22.0.1229.95 m,其他浏览器也没有仔细去测试过,有兴趣的人可以自己测试测试,总结总结,俺这种半吊子菜鸟就是个小混混。。。


此外,顺便把showModalDialog的基本用法也写一写吧,都是网上的
一、基本使用
语法:returnValue=window.showModalDialog(sURL[,vArguments][,sFeatures])
参数说明:
sURL:子窗口的URL。
vArguments:父窗口想要传给子窗口的参数。(子窗口可以通过window.dialogArguments来获取该参数)
sFeatures:描述子窗口的外观信息等。

dialogHeight——子窗口的高度;
dialogWidth——子窗口的宽度;
dialogLeft——子窗口离屏幕左边的距离;
dialogTop——子窗口离屏幕上边的距离;
center——子窗口是否居中显示,默认yes,但仍可以指定高度和宽度(还可以指定dialogLeft和dialogTop了吗?)
help——是否显示帮助按钮,默认yes;
resizable——是否可以被改变大小,默认no;
status——是否显示状态栏,默认yes[Modeless]或者no[Modal](不明白,没体会)
scroll——指明对话框是否显示滚动条,默认为yes

示例:
//打开一个宽高均400px,无状态栏无帮助不能调节大小且居中屏幕的窗口
var sUrl = 'page0.aspx'; 
window.showModalDialog(sUrl, window, "dialogWidth:400px;dialogHeight:400px;status:0;help:0;resizable:0;center:1;");
二、控制父窗口
定义模式窗口时,设定 window 为对话框参数,则在该窗口中,可通过window.dialogArguments来控制父窗口的一切元素。
示例:
var oParent = window.dialogArguments; //获取父窗口对象
oParent.location.reload(); //父窗口刷新,当然还可以做其他操作
三、传值
如最开始解决chrome不能接收子窗口返回值那个例子再好不过啦。

四、提交表单会打开新窗口的问题
哎,这个问题遇到过两次,两次都是问的Green,果然,有些时候,有些知识点,不自己写一下就是记不住啊!古语云“好记性不如烂笔头”,这话是不是要改成“好记性不如码字工”了,囧。。。。。
言归正传啊,这个问题只要在<head>内加入一下代码即可:
<base target="_self"/>
再啰嗦两句啊,其实这个base标签的target属性很早以前就所有了解,也大致知道每个属性值的含义,可是遇到问题就不知道这里用上这么简单的一句就好了,果然光看书是没有用的哇,贵在实践,实践,实在是贱-_-|||

其他还有什么缓存问题、Session丢失问题什么什么的,我都还没用过,写了也是记不住,就不写啦,哎,像我这种懒人,什么时候技术才能有所进步啊!!!

参考资料:
1、showModalDialog模式窗口的若干问题

2、window.open详解

3、Chrome不支持showModalDialog模态对话框和无法返回returnValue的问题