首页 > 代码库 > 神奇的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