在Chrome/Mac上强制DOM重绘/刷新

Jas*_*ter 209 javascript css macos dom google-chrome

每隔一段时间,Chrome都会错误地呈现完全有效的HTML/CSS或根本不呈现.通过DOM检查器深入挖掘通常足以让它实现其方式的错误并正确地重绘,因此可以证明标记是好的.这种情况在我正在进行的项目中经常(并且可预测地)发生,因为我已经将代码放在适当位置以在某些情况下强制重绘.

这适用于大多数浏览器/操作系统组合:

    el.style.cssText += ';-webkit-transform:rotateZ(0deg)'
    el.offsetHeight
    el.style.cssText += ';-webkit-transform:none'
Run Code Online (Sandbox Code Playgroud)

如在,调整一些未使用的CSS属性,然后询问一些强制重绘的信息,然后取消对该属性的破坏.不幸的是,Chrome for Mac背后的聪明团队似乎找到了一种方法来获得offsetHeight而无需重绘.从而杀死了一个有用的黑客.

到目前为止,我想出的最好的方法是在Chrome/Mac上获得同样的效果:

    $(el).css("border", "solid 1px transparent");
    setTimeout(function()
    {
        $(el).css("border", "solid 0px transparent");
    }, 1000);
Run Code Online (Sandbox Code Playgroud)

实际上,强制元素跳跃一点,然后冷却一秒钟然后再跳回来.更糟糕的是,如果你将超时时间降低到500毫秒以下(不太明显),它通常不会产生预期的效果,因为浏览器在返回其原始状态之前不会重新绘制.

有人愿意提供适用于Chrome/Mac的重绘/刷新黑客(最好是基于上面的第一个例子)的更好版本吗?

Jua*_*ank 179

不确定你想要实现什么,但这是我过去使用的一种方法,成功地强制浏览器重绘,也许它会对你有用.

// in jquery
$('#parentOfElementToBeRedrawn').hide().show(0);

// in plain js
document.getElementById('parentOfElementToBeRedrawn').style.display = 'none';
document.getElementById('parentOfElementToBeRedrawn').style.display = 'block';
Run Code Online (Sandbox Code Playgroud)

如果这个简单的重绘不起作用,你可以尝试这个.它将一个空文本节点插入到元素中,以保证重绘.

var forceRedraw = function(element){

    if (!element) { return; }

    var n = document.createTextNode(' ');
    var disp = element.style.display;  // don't worry about previous display style

    element.appendChild(n);
    element.style.display = 'none';

    setTimeout(function(){
        element.style.display = disp;
        n.parentNode.removeChild(n);
    },20); // you can play with this timeout to make it as short as possible
}
Run Code Online (Sandbox Code Playgroud)

编辑:为响应ŠimeVidas,我们在这里取得的成果将是强制回流.你可以从大师那里了解更多信息http://paulirish.com/2011/dom-html5-css3-performance/

  • 而不是$('#parentOfElementToBeRedrawn').hide().show(); 我需要.hide.show(0)才能在Chrome中正常运行 (38认同)
  • 仅供参考,我正在研究的相关领域是无限滚动列表.隐藏/显示整个UL有点不可行,所以我玩了一遍,发现如果你做`.hide().show(0)`on*any*element(我选择页面页脚)它应该刷新页. (6认同)
  • Afaik,无法仅重绘视口的一部分.浏览器总是重绘整个网页. (3认同)
  • .hide().show(0)为我工作,但.hide().show()没有 (2认同)

Vin*_*els 61

上述答案都不适合我.我注意到调整窗口大小确实会导致重绘.所以这对我来说:

$(window).trigger('resize');
Run Code Online (Sandbox Code Playgroud)

  • 提示:这将重绘您的整个窗口,这是性能成本高昂的,可能不是OP想要的 (11认同)
  • 调整窗口大小总是会触发重排,但代码`$(window).trigger('resize')`不会,它只会抛出'resize'事件,如果分配了相关的处理程序,它会触发它. (7认同)
  • 你帮我节省了整整一周的时间!我的问题是设置 <body style="zoom:67%"> 后,页面缩放到我的预期级别,但页面被冻结并且无法滚动!。您的回答立即解决了这个问题 (2认同)

avi*_*sim 28

此解决方案没有超时!真正的力量重绘!适用于Android和iOS.

var forceRedraw = function(element){
  var disp = element.style.display;
  element.style.display = 'none';
  var trick = element.offsetHeight;
  element.style.display = disp;
};
Run Code Online (Sandbox Code Playgroud)

  • 这对我在 Chrome 上不起作用,对 Linux 上的 FF 不起作用。然而,简单地访问 element.offsetHeight (不修改显示属性)**确实**有效。就像当元素不可见时浏览器会跳过 offsetHeight 计算。 (2认同)

mor*_*wry 26

我们最近遇到过这种情况并发现使用translateZ将受影响的元素提升为复合图层可以解决问题而无需额外的javascript.

.willnotrender { 
   transform: translateZ(0); 
}
Run Code Online (Sandbox Code Playgroud)

由于这些绘画问题主要出现在Webkit/Blink中,并且此修复主要针对Webkit/Blink,因此在某些情况下更为可取.特别是因为许多JavaScript的解决方案,导致回流和重绘,而不仅仅是一个重绘.

  • 当问题是在画布上绘制已删除的 HTML 元素时,即使画布连续进行动画处理,这对我来说效果很好。 (2认同)
  • 这有效。Safari 将 div 中的元素设置为不显示、可见且不可选择。调整窗口大小使它们消失。添加此变换会导致“伪影”按预期消失。 (2认同)
  • 这太感谢了!有效。现在是 2020 年,这个黑客攻击是从 2015 年 1 月开始的。知道为什么这个问题没有得到解决吗? (2认同)

zup*_*upa 13

这适合我.荣誉来到这里.

jQuery.fn.redraw = function() {
    return this.hide(0, function() {
        $(this).show();
    });
};

$(el).redraw();
Run Code Online (Sandbox Code Playgroud)

  • @zupa它已经过测试并在Chrome 38上工作.诀窍是`show()`与`show(0)`的不同之处在于,通过在后者中指定持续时间,它是由超时的jQuery动画方法触发而不是立即触发这给了时间以`hide(0,function(){$(this).show();})`的方式呈现. (2认同)

Bra*_*son 9

隐藏元素然后在setTimeout为0的情况下再次显示它将强制重绘.

$('#page').hide();
setTimeout(function() {
    $('#page').show();
}, 0);
Run Code Online (Sandbox Code Playgroud)


小智 6

这似乎对我有用。另外,它真的根本不显示。

$(el).css("opacity", .99);
setTimeout(function(){
   $(el).css("opacity", 1);
},20);
Run Code Online (Sandbox Code Playgroud)


J.P*_*vet 6

2020:更轻更强

以前的解决方案对我来说不再适用。

我猜想浏览器通过检测越来越多的“无用”更改来优化绘图过程。

该解决方案使浏览器绘制一个克隆来替换原始元素。它有效并且可能更可持续:

const element = document.querySelector('selector');
if (element ) {
  const clone = element.cloneNode(true);
  element.replaceWith(clone);
}
Run Code Online (Sandbox Code Playgroud)

在 Chrome 80 / Edge 80 / Firefox 75 上测试

  • 如果您的元素连接了事件侦听器,那么这不是一个好主意。替换元素后,您必须将它们重新添加到元素中。 (4认同)