web开发中,大家有可能经历过下面这种问题:
① 画面上通过setTimeout启动了一个定时器,用以动态更新画面上的某个组件(比如,1 秒刷新一次时间中的秒数)
② 当页面弹出一个模态对话框(通过 showModalDialog打开)时候,你会发现画面上的setTimeout失效了(比如,时间不再是一秒刷新一次了)
发生这个问题的原因大家可能都知道:模态对话框的特性导致了这个问题。在关闭模态对话框之前,用户无法将父窗口进行任何操作。而且Javascript又是单线程的,导致在模态对话框打开之后,被启动的setTimeout定时器,即时到了指定时间之后,也没有机会被执行。
那么,如果解决这个问题呢?
目前,我共找到两个解决方案,本篇blog中先描述方案一。下面开始。
方案一是采用 iframe 来实现的。
该方案的大体思路如下:
① 新建一个主HTML文件,如上面的index.html,该文件仅仅用于存放两个iframe,代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>setTimeout无效的解决方案(一)</title>
</head>
<body>
<!-- 这个iframe用来显示画面内容 -->
<div id="frameDiv1">
<iframe id="dlgFrame1" name="dlgFrame1" src="frame1.html" style="width: 100%; height: 100%; border: none;"></iframe>
</div>
<!-- 这个iframe仅仅用于打开模态对话框,所以将其隐藏 -->
<div id="frameDiv2" style="display: none;">
<iframe id="dlgFrame2" name="dlgFrame2" src="frame2.html"></iframe>
</div>
</body>
</html>
② 新建一个iframe,如上面的iframe1.html,此文件用来展示你想在画面上显示的东西,代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script>
function start() {
(event.target || event.srcElement)["disabled"] = true;
setTimeout(function(){
document.querySelector("#txt").innerText = Math.random();
setTimeout(arguments.callee, 1000);
}, 1000);
}
function dlgOpenButton() {
// 获取iframe2的window对象,通过此window对象打开模态对话框
document.querySelector("#txt2").innerText = "模态画面打开中。。。";
var win = window.top.document.querySelector("#dlgFrame2").contentWindow;
var val = win.showModalDialog("dialog.html",Math.random());
document.querySelector("#txt2").innerText = "模态画面关闭了,返回值为: " + val;
// 如果是通过alert来打开的话,仍然是不行的。这个目前还无解。
//var val = win.alert("modal dialog");
//document.querySelector("#txt2").innerText = "模态画面关闭了,返回值为: " + val;
}
</script>
</head>
<body>
<button type="button" οnclick="start()">启动定时器</button>
<button type="button" οnclick="dlgOpenButton()">打开模态对话框</button>
<div>
<p style="float: left;">定时更新内容:</p>
<p id="txt" style="float: left;"></p>
</div>
<div style="float: left; clear: left;">
<p style="float: left;">模态对话框状态:</p>
<p id="txt2"></p>
</div>
</body>
</html>
③ 新建另一个iframe,如上面的iframe2.html,此文件仅仅用于打开模态对话框,代码如下:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
</body>
</html>
④ 最后,要打开的模态对话框的代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我是模态对话框</title>
<script>
window.onload = function() {
var para = window.dialogArguments;
document.querySelector("#fromParent").innerText = para;
};
window.onunload = function() {
window.returnValue = Math.random();
};
</script>
</head>
<body>
<div>
<p>我是模态对话框,父画面传给我的值是: </p>
<p id="fromParent"></p>
</div>
<br/>
</body>
</html>
上面将相关代码添完了,下面简单说一下此方案的思路:首先,我们是准备在iframe1里启动定时器的,所以,我们为了不让iframe1中的定时器失效,我们就不能再在iframe1中打开模态对话框,理由在上面已经描述过了。这样一来,我们只能寻找另外一种方案,使得能够打开模态对话框,同时又不阻断iframe1中的定时器。这样想的话,我们只好借用一下其他window对象,并调用其showModalDialog方法来打开模态对话框。所以,iframe2就派上用场了。即,在iframe1中,获取到iframe2的window对象,然后打开模态对话框,这种方式打开的模态对话框是不会阻断iframe1中的setTiemout的。
大家可以打开附件iframe.zip,然后运行其中的 index.html,如下图。然后先点击“启动定时器”按钮,然后再点击“打开模态对话框”按钮,观察一下画面,便可验证。
PS:
① 以上方案仅在IE10中进行过了验证。
② 对于alert之类的对话框上述方案无效。