强制DOM重绘/刷新Chrome/Mac

每隔一段时间,Chrome会不正确地呈现完全有效的HTML/CSS或根本不呈现。深入DOM检查器通常足以让它意识到其方法的错误并正确地重新绘制,因此可以证明标记是好的。在我所从事的项目中,这种情况经常发生(并且可以预见),以至于我已经在某些情况下放置了代码来强制重绘。

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

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

例如,调整一些未使用的CSS属性,然后要求一些强制重绘的信息,然后取消调整属性。不幸的是,Chrome for Mac背后的聪明团队似乎已经找到了一种方法,可以在不重新绘制的情况下获得这种offsetHeight。这样就杀死了一个有用的黑客。

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

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

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

有人愿意提供一个更好的版本的重绘/刷新hack(最好基于上面的第一个例子),工作在Chrome/Mac?

362558 次浏览

不确定你到底想要达到什么目的,但这是我过去使用的一种方法,成功地迫使浏览器重新绘制,也许它对你有用。

// 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/

这对我很有用。荣誉在这里

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


$(el).redraw();

一种在IE上对我有效的方法(我不能使用显示技术,因为输入不能散焦)

如果你的边距为0,它也可以工作(改变填充也可以)

if(div.style.marginLeft == '0px'){
div.style.marginLeft = '';
div.style.marginRight = '0px';
} else {
div.style.marginLeft = '0px';
div.style.marginRight = '';
}

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

$('#page').hide();
setTimeout(function() {
$('#page').show();
}, 0);

调用window.getComputedStyle()将强制回流

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

var forceRedraw = function(element){
var disp = element.style.display;
element.style.display = 'none';
var trick = element.offsetHeight;
element.style.display = disp;
};

CSS。这适用于删除或添加子元素的情况。在这些情况下,边界和圆角可能会留下工件。

el:after { content: " "; }
el:before { content: " "; }

上面的答案对我都没用。我确实注意到调整我的窗口确实导致重绘。这对我来说很管用:

$(window).trigger('resize');

Html:示例

<section id="parent">
<article class="child"></article>
<article class="child"></article>
</section>

Js:

  jQuery.fn.redraw = function() {
return this.hide(0,function() {$(this).show(100);});
// hide immediately and show with 100ms duration


};

调用函数:

$('article.child').redraw(); //<==坏主意

$('#parent').redraw();

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

.willnotrender {
transform: translateZ(0);
}

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

我对IE10 + IE11的修复。基本上发生的情况是在必须重新计算的包装元素中添加DIV。然后去掉它,瞧;工作就像一个魅力:)

    _initForceBrowserRepaint: function() {
$('#wrapper').append('<div style="width=100%" id="dummydiv"></div>');
$('#dummydiv').width(function() { return $(this).width() - 1; }).width(function() { return $(this).width() + 1; });
$('#dummydiv').remove();
},

如果你想保留样式表中声明的样式,最好使用如下的方法:

jQuery.fn.redraw = function() {
this.css('display', 'none');
var temp = this[0].offsetHeight;
this.css('display', '');
temp = this[0].offsetHeight;
};


$('.layer-to-repaint').redraw();

注意:使用latest webkit/blink,存储offsetHeight的值是强制的,以触发重画,否则VM(出于优化目的)可能会跳过该指令。

更新:增加了第二个offsetHeight读取,这是必要的,以防止浏览器在恢复display值时排队/缓存接下来的CSS属性/类更改(这样可以呈现接下来的CSS转换)

这似乎对我有用。另外,它根本就看不出来。

$(el).css("opacity", .99);
setTimeout(function(){
$(el).css("opacity", 1);
},20);

大多数应答需要使用异步超时,这会导致恼人的闪烁。

但我想到了这个,它很顺利,因为它是同步的:

var p = el.parentNode,
s = el.nextSibling;
p.removeChild(el);
p.insertBefore(el, s);

这是我解决内容消失的方法……

<script type = 'text/javascript'>
var trash_div;


setInterval(function()
{
if (document.body)
{
if (!trash_div)
trash_div = document.createElement('div');


document.body.appendChild(trash_div);
document.body.removeChild(trash_div);
}
}, 1000 / 25); //25 fps...
</script>

我今天在Chrome v51的OSX El Capitan中遇到了这个挑战。这个页面在Safari中运行正常。我尝试了本页上几乎所有的建议——没有一个是正确的——所有的都有副作用……我最终实现了下面的代码——超级简单——没有副作用(在Safari中仍然像以前一样工作)。

解决方案:根据需要切换有问题元素上的类。每次切换都会强制重画。(我使用jQuery是为了方便,但普通的JavaScript应该没有问题…)

jQuery类切换

$('.slide.force').toggleClass('force-redraw');

CSS类

.force-redraw::before { content: "" }

就是这样……

注意:为了看到效果,你必须运行“Full Page”下面的代码片段。

$(window).resize(function() {
$('.slide.force').toggleClass('force-redraw');
});
.force-redraw::before {
content: "";
}
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
}
.slide-container {
width: 100%;
height: 100%;
overflow-x: scroll;
overflow-y: hidden;
white-space: nowrap;
padding-left: 10%;
padding-right: 5%;
}
.slide {
position: relative;
display: inline-block;
height: 30%;
border: 1px solid green;
}
.slide-sizer {
height: 160%;
pointer-events: none;
//border: 1px solid red;


}
.slide-contents {
position: absolute;
top: 10%;
left: 10%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<p>
This sample code is a simple style-based solution to maintain aspect ratio of an element based on a dynamic height.  As you increase and decrease the window height, the elements should follow and the width should follow in turn to maintain the aspect ratio.  You will notice that in Chrome on OSX (at least), the "Not Forced" element does not maintain a proper ratio.
</p>
<div class="slide-container">
<div class="slide">
<img class="slide-sizer" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
<div class="slide-contents">
Not Forced
</div>
</div>
<div class="slide force">
<img class="slide-sizer" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
<div class="slide-contents">
Forced
</div>
</div>
</div>

function resizeWindow(){
    var evt = document.createEvent('UIEvents');
    evt.initUIEvent('resize', true, false,window,0);
    window.dispatchEvent(evt);
}

在500毫秒后调用这个函数。

我也遇到过类似的问题,这一行JS代码帮我解决了问题:

document.getElementsByTagName('body')[0].focus();

在我的情况下,这是一个错误与Chrome扩展不重新绘制页面后改变其CSS从扩展。

我想返回所有的状态到以前的状态(无需重新加载),包括jquery添加的元素。上面的实现不能工作。我是这样做的。

// Set initial HTML description
var defaultHTML;
function DefaultSave() {
defaultHTML = document.body.innerHTML;
}
// Restore HTML description to initial state
function HTMLRestore() {
document.body.innerHTML = defaultHTML;
}






DefaultSave()
<input type="button" value="Restore" onclick="HTMLRestore()">

我有一个react组件列表,当滚动时,然后打开另一个页面,然后返回时,列表不会在Safari iOS上呈现,直到页面被滚动。这就是解决办法。

    componentDidMount() {
setTimeout(() => {
window.scrollBy(0, 0);
}, 300);
}

下面的css适用于我的IE 11和Edge,不需要JS。 scaleY(1)在这里起作用。这似乎是最简单的解决方案

.box {
max-height: 360px;
transition: all 0.3s ease-out;
transform: scaleY(1);
}
.box.collapse {
max-height: 0;
}

它帮助了我

domNodeToRerender.style.opacity = 0.99;
setTimeout(() => { domNodeToRerender.style.opacity = '' }, 0);

2020:更轻更强

以前的解决办法对我已经不管用了。

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

这个解决方案使浏览器绘制一个克隆来替换原来的元素。它很有效,而且可能更可持续:

const element = document.querySelector('selector');
if (element ) {
const clone = element.cloneNode(true);
element.replaceWith(clone);
}

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

2020年10月,Chrome 85仍然存在该问题。

morewry的使用transform:translateZ(0)的解决方案,实际上,任何转换工作,包括translate(0,0)和scale(1),但如果你必须再次更新元素,那么技巧是切换转换,最好的方法是直接删除它,在一帧后,使用requestAnimationFrame (setTimeout应该总是避免,因为它会更慢,所以它可能会导致故障)。

updateone元素:

    function refresh_element(node) {
// you can use scale(1) or translate(0, 0), etc
node.style.setProperty('transform', 'translateZ(0)');
// this will remove the property 1 frame later
requestAnimationFrame(() => {
node.style.removeProperty('transform');
});
}

这对我来说在20毫秒后工作:

const forceRedraw = elem => {
const t = elem.ownerDocument.createTextNode(' ') ;
elem.appendChild(t) ;
setTimeout(() => { elem.removeChild(t) }, 0) ;
}