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

IT技术 javascript css macos dom google-chrome
2021-01-14 15:51:07

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

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

    el.style.cssText += ';-webkit-transform:rotateZ(0deg)'
    el.offsetHeight
    el.style.cssText += ';-webkit-transform:none'

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

到目前为止,我想出的在 Chrome/Mac 上获得相同效果的最佳方法是这段丑陋的内容:

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

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

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

6个回答

不确定您想要实现的确切目标,但这是我过去成功使用的一种方法,可以强制浏览器重绘,也许它对您有用。

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

// in plain js
document.getElementById('parentOfElementToBeRedrawn').style.display = 'none';
document.getElementById('parentOfElementToBeRedrawn').style.display = 'block';

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

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
}

编辑:为了回应 Šime Vidas,我们在这里实现的是强制回流。你可以从大师本人那里了解更多http://paulirish.com/2011/dom-html5-css3-performance/

Afaik,不可能只重绘视口的一部分。浏览器总是重绘整个网页。
2021-03-16 15:51:07
可以仅重绘视口的一部分。没有 API 可以这样做,但浏览器可以选择这样做。视口一开始是用瓷砖绘制的。并且复合层是独立绘制的。
2021-03-23 15:51:07
@l.poellabauer 在这里也是一样 - 这没有意义......但是谢谢。你他妈是怎么发现的???:)
2021-03-24 15:51:07
仅供参考,我正在研究的相关领域是一个无限滚动列表。隐藏/显示整个 UL 有点不可行,所以我玩了一下,发现如果你.hide().show(0)任何元素(我选择了页脚)它应该刷新页面。
2021-03-26 15:51:07
而不是 $('#parentOfElementToBeRedrawn').hide().show(); 我需要 .hide.show(0) 才能在 Chrome 中正常工作
2021-04-09 15:51:07

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

$(window).trigger('resize');
你为我节省了整整一个星期!我的问题是在设置 <body style="zoom:67%"> 后,页面缩放到我预期的水平,但页面被冻结并且无法滚动!。您的回答立即解决了这个问题
2021-03-16 15:51:07
提示:这将重绘您的完整窗口,这是性能成本高,可能不是 OP 想要的
2021-03-26 15:51:07
调整窗口大小总是会触发重排,但代码$(window).trigger('resize')不会,它只会抛出“调整大小”事件,如果分配了相关处理程序,则会触发该事件。
2021-04-06 15:51:07

我们最近遇到了这个问题,并发现使用translateZ将受影响的元素提升到复合层可以解决这个问题,而无需额外的 javascript。

.willnotrender { 
   transform: translateZ(0); 
}

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

这是非常感谢!有效。现在是 2020 年,这次黑客攻击是从 15 年 1 月开始的。知道为什么这个问题没有得到解决吗?
2021-03-10 15:51:07
当问题是在画布上绘制已删除的 HTML 元素时,这对我来说效果很好,即使画布是连续动画的。
2021-03-13 15:51:07
这奏效了。Safari 留下了在 div 中设置为不显示、可见和不可选择的元素。调整窗口大小使它们消失。添加此转换会导致“工件”按预期消失。
2021-03-23 15:51:07
z-index: 0;, 似乎也能正常工作,与transform此答案中的相比,这会导致更少的 z 分层中断(在我的用例中)
2021-04-02 15:51:07
这修复了我遇到的布局问题neon-animated-pages谢谢:+1:
2021-04-06 15:51:07

这个解决方案没有超时!重绘!适用于 Android 和 iOS。

var forceRedraw = function(element){
  var disp = element.style.display;
  element.style.display = 'none';
  var trick = element.offsetHeight;
  element.style.display = disp;
};
这在 Chrome 或 Linux 上的 FF 上对我不起作用。然而,简单地访问 element.offsetHeight (不修改 display 属性)确实有效。当元素不可见时,浏览器会跳过 offsetHeight 计算。
2021-03-29 15:51:07
此解决方案非常适合解决我在“overwolf”中使用的基于 chrome 的浏览器中遇到的错误。我试图弄清楚如何做到这一点,而你为我省了力气。
2021-04-05 15:51:07

这对我有用。去这里点个赞

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

$(el).redraw();
这应该与 $().hide().show(0)
2021-03-19 15:51:07
@Mahn 你试过了吗?在我的猜测中 .hide() 是非阻塞的,因此它会跳过浏览器中的重新渲染运行,也就是方法的全部内容。(如果我没记错的话,我当时测试了它。)
2021-03-19 15:51:07
@Mahn 好吧,那很好。如果它跨平台工作并且不是最近的 jQuery 功能,我绝对支持您的解决方案。由于我没有时间对其进行测试,因此我将其添加为“可能有效”。如果您认为上述情况适用,欢迎您编辑答案。
2021-03-25 15:51:07
但它留下了一些“残留物”,例如“显示:块”等,有时会破坏页面结构。
2021-03-27 15:51:07
@zupa它的测试,并在Chrome 38工作的诀窍是,show()不同于show(0)在通过指定在后者的持续时间,它是由一个超时的jQuery动画方法触发而立即赋予时间以在以同样的方式呈现hide(0, function() { $(this).show(); })呢.
2021-04-01 15:51:07