在Javascript DOM操作后强制Internet Explorer刷新DOM。

28

这是情况。我有一些类似于这样的javascript代码:

function onSubmit() {
    doSomeStuff();
    someSpan.style.display="block";
    otherSpan.style.display="none";
    return doLongRunningOperation;
}

当我将此设置为表单提交操作并在非IE浏览器中运行时,它会迅速交换两个span的可见性并运行长时间的javascript操作。如果我在IE中执行此操作,则直到onSubmit()完全返回后才进行交换。

我可以通过添加一个alert框来强制进行DOM重绘,如下所示:

function onSubmit() {
    doSomeStuff();
    someSpan.style.display="block";
    otherSpan.style.display="none";
    alert("refresh forced");
    return doLongRunningOperation;
}

另外,明显的jQuery重构并不会影响IE的行为:

function onSubmit() {
    doSomeStuff();
    $("#someSpan").show();
    $("#otherSpan").hide();
    return doLongRunningOperation;
}

这种行为存在于IE8和IE6上。有没有办法在这些浏览器中强制重新绘制DOM?


首先,在longRunningOperation中你在做什么? - meder omuraliev
你尝试过将可见性切换移动到自己的函数中吗? - seth
longRunningOperation正在进行表单验证。在少数情况下,该表单过于庞大。 - Justin Dearing
6个回答

18

Mozilla(以及可能也包括IE)会缓存/延迟执行影响显示的DOM更改,以便它可以一次计算所有更改,而不是在每个语句之后重复计算。

要强制更新(以强制立即同步重新流动或重新布局),你的JavaScript应该读取受更改影响的属性,例如someSpan和otherSpan的位置。

(这个Mozilla实现细节在视频《更快的HTML和CSS:Web开发人员的布局引擎内部》中提到。)


有趣。我不知道这个。 - meder omuraliev
我会在明天早上尝试一下。 - Justin Dearing
2
在我引用的 Mozilla 视频中,视频时间为 37:15,他建议询问“offset top”或“offset left”,这需要布局保持最新状态。 - ChrisW
非常感谢!!这个救了我。顺便说一下,那个做视频的家伙讲话很糟糕。 - HaxElit

9

继续ChrisW所说的:

这里有一个刷新DOM的脚本,所以你不必调用alert(""); (来自http://amolnw.wordpress.com/category/programming/javascript/):

function flushThis(id){
   var msie = 'Microsoft Internet Explorer';
   var tmp = 0;
   var elementOnShow = document.getElementById(id);
   if (navigator.appName == msie){
      tmp = elementOnShow.parentNode.offsetTop  +  'px';
   }else{
      tmp = elementOnShow.offsetTop;
   }
}

它对我有用!!! 感谢您的建议。


5

我在Chrome 21中遇到了一个问题,拖动一个带有下降字母(“g”)的单词会在屏幕上留下一条虫子尘的痕迹,这些痕迹会在下一次屏幕刷新时消失。ChrisW的解决方案(询问一个布局敏感的属性)没有起作用。

有效的方法是在页面顶部添加一个1像素的空白div,然后在毫秒后通过调用以下函数从拖动操作结束:

// Needed by Chrome, as of Release 21. Triggers a screen refresh, removing drag garbage.
function cleanDisplay() {
    var c = document.createElement('div');
    c.innerHTML = 'x';
    c.style.visibility = 'hidden';
    c.style.height = '1px';
    document.body.insertBefore(c, document.body.firstChild);
    window.setTimeout(function() {document.body.removeChild(c)}, 1);
}

注意:你需要延迟。仅仅添加和删除div是无效的。此外,div需要添加在需要重新绘制的页面部分之上。


这个解决方案是否比我之前异步调用函数的方法更好呢? - Justin Dearing
在IE10中屏幕刷新出现问题,而这个方法解决了问题,其他方法都不行。 - Maxime Fafard

3

您也可以将长期运行的函数包装在setTimeout(function(){longTerm();},1);中。


2

在IE10中,element.focus()对我有效。

function displayOnOff(){
    var elm = document.getElementById("myDiv");
    elm.style.display="block";
    elm.focus();
    for(var i=0; i<1000000; i++){
        console.log("waiting...............");
    }
    elm.style.display = "none";
}

2

你的longRunningOperation是否可以异步调用?


2
你最终是正确的,但需要一些工作。我异步地调用了长时间运行的操作,让我的提交事件返回 false,然后异步事件真正地提交了我的表单。这样可以在所有浏览器中实现正确的行为,但需要大量的工作,感觉有点笨拙。 - Justin Dearing
1
可能会感觉有些笨拙,但客户端应用程序的最佳设计是保持浏览器响应。当您启动长时间运行的进程时,会挂起浏览器(绝对不要这样做!),因此最好花时间设计如何管理异步操作。无论如何...祝您玩得愉快! - misteraidan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接