loading...

3

js更新dom后的强制刷新问题

javascript读完大概需要11分钟

  • 发布时间:2017-08-17 17:12 星期四
  • 刘伟波
  • 197
  • 更新于2017-08-17 17:12 星期四

js更新dom是web系统中经常出现的场景,但是有时候可能会遇到这样的情况,在更新dom之后还执行了一段运行时间可能比较长的js代码,这时你会发现,你更新的dom不会立刻在页面显现出来,而要等所有js都执行完之后才能出现。考虑以下的代码

[html] view plain copy

  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <meta charset="gbk" />  
  5.         <title>Test Refresh</title>  
  6.         <script src="js/jquery-1.7.1.js"></script>  
  7.         <script type="text/javascript">  
  8.                   
  9.                 function runLongOperation(){  
  10.                     var d = new Date().getTime();  
  11.                         while (1) {  
  12.                             var current = new Date().getTime();  
  13.                             if (current - d > 3000) {  
  14.                                 console.log("good");  
  15.                                 break;  
  16.                             }  
  17.                         }  
  18.                 }  
  19.                   
  20.                 $(function(){  
  21.                     $("#submit1").click(function(){  
  22.                         var $n = $("#name");  
  23.                         $n.hide();  
  24.                         runLongOperation();  
  25.                     });  
  26.                 });  
  27.         </script>  
  28.     </head>  
  29.     <body>  
  30.             <input type="text" placeholder="please input your name" name="wd" id="name"/>  
  31.             <input type="button" name="submit1" id="submit1" value="Hide"/>  
  32.     </body>  
  33. </html>  


点击按钮后首先执行的操作是隐藏输入框name,然后执行了一段长约3秒钟的操作,你会发现点击按钮后隐藏输入框的动作并不会立刻在浏览器上体现,而是等到runLongOperation执行完之后才发生(我试了在chrome,FF和IE上均是如此)。为什么会这样呢,在stackoverflow上找到一个大神的解释,“Mozilla (maybe IE as well) will cache/delay executing changes to the DOM which affect display, so that it can calculate all the changes at once instead of repeatedly after each and every statement.”。也就是说,浏览器会cache住影响dom展现的操作,直到所有的js都执行完,这时候它可以一次性地更新需要更新的dom,这样做可能是出于性能的考虑。

通常来说,这不会有什么问题,但有的时候也许会带来一些体验上的不同。以我最近接触的一个系统的场景来说,点击一个tab页签会加载一个页面,加载完后会执行先关的初始化js,也就是在set完这个tab容器的content后还要执行一段js。不幸的是,这段js执行的时间有点长(由于历史原因这段js必须执行),大约1~2秒的时间(IE下),所以用户会在点击tab页签后并不能马上看到页面,而是看到一个空白的区域并等待一段时间才能看到正常的画面,这就给了用户不好的体验。为了消除这个影响,我们想要强制浏览器让内容先展示出来,再执行剩下的js,使用户可较快地看到结果,虽然接下来js的执行的js也会影响用户的操作(单线程),但因为用户在点开页面后通常有段时间是不做任何操作的(通常要先看一眼什么的),这段时间可以让坑爹的js执行完。

接下来的问题是如何让浏览器强制刷新更新后的dom呢,我们找到了几种方法:

1.更新dom的js执行完后执行alert,会强制浏览器刷新dom。这个方法简单有效,但无可操作性,不可能在系统中无缘无故弹出一个alert框。

2.根据stackoverflow上某位大神的说法,“To force an update (to force an immediate, synchronous reflow or relayout), your JavaScript should read a property that's affected by the change, e.g. the location of someSpan and otherSpan.:”,即是如果让js去读被改变的dom节点的相关属性,则可以迫使浏览器更新dom。但遵照这个思想试验只好发现没有起效,不知是否我的操作方法不当??望有高人指点!!

3.借助setTimeout,把后面影响刷新的js放在setTimeout里面执行。按我的理解,原来的一大段js是处在一个同步执行的逻辑中的,而浏览器会延迟一个dom刷新的机制也只是作用在同步代码中,如果我们将后面的js放在setTimeout里面,那相当于后面的代码处在一个异步逻辑中,浏览器就会认为当前的同步逻辑已经完成,可以刷新更改的dom了。但是,setTimeout的时间也不能设得太小,如果设为0,你会发现仍然可能不能立刻刷新更改后的dom。我想也许这跟各个浏览器的任务调度有关,当前的同步逻辑执行完之后,浏览器刷新dom的操作可能会被另一个任务抢占了,经试验把timeout的时间设在100左右较为理想。。。顺带提一句的是,js引擎都是单线程处理任务队列的,任何异步编程都是障眼法,可参考http://www.cnblogs.com/jeffwongishandsome/archive/2011/06/13/2080145.html

最后,我的解决方案是将之前的代码改成如下形式:

[javascript] view plain copy

  1. $(function(){  
  2. $("#submit1").click(function(){  
  3.    var $n = $("#name");  
  4.    $n.hide();  
  5.    setTimeout(runLongOperation, 100);  
  6. })  
  7. })  



你可能感兴趣的文章

    发表评论

    评论支持markdown,评论内容不能超过500字符,如果内容过多或者要及时回复,建议去 平台,一般一天之内就会回复。
    关于技术问题或者有啥不懂的都可以留言,我会定期回复答 疑,推荐最新仓库 前端知识体系, 感謝支持!