背景:
在一个页面中我们需要利用动态创建iframe并添加相应的CSS文件来实现网页的局部打印, 于是就有了下面这段代码:
1 | |
2 |
$printDetailFrame = $(‘<iframe>‘, {
|
3 |
src: "",
|
4 |
id: "printFrame",
|
5 |
frameborder: 0,
|
6 |
scrolling: "no",
|
7 |
style: "display:none"
|
8 |
}).appendTo("body")
|
9 |
紧接着我们再把CSS添加到这个动态创建的iframe,
1 | |
2 |
$printCSSReset = $("link[href*=‘reset.css‘]").clone(),
|
3 |
$printCSSReports = $("link[href*=‘reports.css‘]").clone(),
|
4 |
$printCSSUnf = $("link[href*=‘unf.css‘]").clone();
|
5 |
$printDetailFrame
|
6 |
.contents()
|
7 |
.find("head")
|
8 |
.append($printCSSReset,$printCSSReports,$printCSSUnf);
|
9 |
发现问题:
到目前为止, 一切都看起来很正常, 然后在需要打印时候
1 | |
2 |
//把要打印的区域加入到iframe内部
|
3 |
......
|
4 |
$printDetailFrame.get(0).contentWindow.print()
|
5 |
在Chrome上测试时候, 打印可以达到预想效果,也就是说我们插入的CSS链接起到了作用, 但是在Firefox 30.0上测试时发现, 打印完全没有预想中的效果, 也就是CSS根本没有起到作用,经过检查动态创建的iframe里面没有任何css添加进去, 这是神马情况?
原来我们忽略了一个问题:
我们在添加CSS代码是在创建iframe之后立即运行的, 但是我们并不能保证iframe已经在页面DOM上创建完成了,
若是在没有创建好iframe的情况下, 在iframe上添加CSS链接就是一个没有实际运行结果的代码, 因为这时候根本就找不到那个iframe.
如何解决呢? 这时候就该我们的主角setTimeout登场了。
解决:
我们把添加CSS那段代码放入一个setTimeout里面, 如下:
1 | |
2 |
setTimeout(function(){
|
3 |
$printDetailFrame
|
4 |
.contents()
|
5 |
.find("head")
|
6 |
.append($printCSSReset,$printCSSReports,$printCSSUnf);
|
7 |
}, 0);
|
8 |
分析:
当我们在页面上动态创建一个iframe时候, 实际上是通知浏览器内核页面渲染进程去创建, 并在js自己的事件列队中插入了一个回调等待,
但是js把这个事情抛给浏览器后就直接继续运行下面的添加CSS代码, 或许是每个浏览器进程调度机制不同,
在firefox就出现了我们上面说的这个问题, 那么ssetTimeout(func, 0)是起到了什么作用呢?
它其实就是把func这个方法加入到事件列队中,这样等iframe在页面上创建完毕, js队列就寻找下个回调也就是func这个函数来运行,
这样我们就让这两个有依赖关系的操作按顺序进行.
参考阅读:
http://blog.csdn.net/kongls08/article/details/6996518
http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
最后的补充:
我们也可以尝试利用jQuery的deffered机制来代替setTimeout, 但这个还需要测试才能确定可行性:
1 | |
2 |
$printDetailFrame.promise().done(function(){
|
3 |
$printDetailFrame
|
4 |
.contents()
|
5 |
.find("head")
|
6 |
.append($printCSSReset,$printCSSReports,$printCSSUnf);
|
7 |
});
|
8 |