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

js 专栏收录该内容
8 篇文章 0 订阅

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

<!DOCTYPE html>
<html>
	<head>
		<meta charset="gbk" />
		<title>Test Refresh</title>
		<script src="js/jquery-1.7.1.js"></script>
		<script type="text/javascript">
				
				function runLongOperation(){
					var d = new Date().getTime();
						while (1) {
							var current = new Date().getTime();
							if (current - d > 3000) {
								console.log("good");
								break;
							}
						}
				}
				
				$(function(){
					$("#submit1").click(function(){
						var $n = $("#name");
						$n.hide();
						runLongOperation();
					});
				});
		</script>
	</head>
	<body>
			<input type="text" placeholder="please input your name" name="wd" id="name"/>
			<input type="button" name="submit1" id="submit1" value="Hide"/>
	</body>
</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

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

$(function(){
$("#submit1").click(function(){
   var $n = $("#name");
   $n.hide();
   setTimeout(runLongOperation, 100);
})
})

  • 1
    点赞
  • 2
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值