首页 > 代码库 > 神奇的setTime(func, 0)
神奇的setTime(func, 0)
背景:
在一个页面中我们需要利用动态创建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 |