每隔一段时间,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 的更好版本的重绘/刷新黑客(最好基于上面的第一个示例)吗?
不确定您要实现的确切目标,但这是我过去成功使用的一种方法,可以强制浏览器重绘,也许它对您有用。
// 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/
以上答案都不适合我。我确实注意到调整窗口大小确实会导致重绘。所以这为我做了:
$(window).trigger('resize');
$(window).trigger('resize')
不会,它只会抛出触发相关处理程序的“resize”事件(如果已分配)。
我们最近遇到了这个问题,发现使用 translateZ 将受影响的元素提升为 composite layer 可以解决问题,而无需额外的 javascript。
.willnotrender {
transform: translateZ(0);
}
由于这些绘画问题主要出现在 Webkit/Blink 中,并且此修复主要针对 Webkit/Blink,因此在某些情况下更可取。特别是因为许多 JavaScript 解决方案会导致 reflow and repaint,而不仅仅是重绘。
neon-animated-pages
时遇到的布局问题。谢谢 :+1:
z-index: 0;
,似乎也有效,与此答案中的 transform
相比,它导致的 z 分层中断(在我的用例中)更少。
这个解决方案没有超时!真力重绘!适用于 Android 和 iOS。
var forceRedraw = function(element){
var disp = element.style.display;
element.style.display = 'none';
var trick = element.offsetHeight;
element.style.display = disp;
};
这对我有用。 Kudos go here。
jQuery.fn.redraw = function() {
return this.hide(0, function() {
$(this).show();
});
};
$(el).redraw();
$().hide().show(0)
基本相同
show()
与 show(0)
的不同之处在于,通过在后者中指定持续时间,它由 jQuery 动画方法在超时而不是立即触发,从而有时间呈现以与 hide(0, function() { $(this).show(); })
相同的方式。
隐藏一个元素,然后在 setTimeout 为 0 内再次显示它会强制重绘。
$('#page').hide();
setTimeout(function() {
$('#page').show();
}, 0);
myElement.getBoundingClientRect()
的值时对我有用的方法,可能是出于优化/性能目的。我只是为将新元素添加到 DOM 并在从 DOM 中删除具有先前(缓存)值的旧元素后获取新值的函数添加了一个零毫秒超时。
这似乎对我有用。另外,它真的根本不显示。
$(el).css("opacity", .99);
setTimeout(function(){
$(el).css("opacity", 1);
},20);
setTimeout(function(){},0);
添加到组合中,以便稍后进行调整。
调用 window.getComputedStyle()
应该强制重排
offsetHeight
属性。
2020:更轻、更强
以前的解决方案对我来说不再适用。
我猜浏览器通过检测越来越多的“无用”更改来优化绘图过程。
此解决方案使浏览器绘制一个克隆以替换原始元素。它有效并且可能更可持续:
const element = document.querySelector('selector');
if (element ) {
const clone = element.cloneNode(true);
element.replaceWith(clone);
}
在 Chrome 80 / Edge 80 / Firefox 75 上测试
我今天在使用 Chrome v51 的 OSX El Capitan 中遇到了这个挑战。有问题的页面在 Safari 中运行良好。我尝试了这个页面上的几乎所有建议 - 没有一个正确 - 所有都有副作用......我最终实现了下面的代码 - 超级简单 - 没有副作用(在 Safari 中仍然像以前一样工作)。
解决方案:根据需要在有问题的元素上切换一个类。每个切换将强制重绘。 (为了方便,我使用了 jQuery,但原生 JavaScript 应该没问题......)
jQuery 类切换
$('.slide.force').toggleClass('force-redraw');
CSS 类
.force-redraw::before { content: "" }
就是这样……
注意:您必须运行“整页”下方的代码段才能看到效果。
$(window).resize(function() { $('.slide.force').toggleClass('force-redraw'); }); .force-redraw::before { 内容:“”; } html, 身体 { 高度: 100%;宽度:100%;溢出:隐藏; } .slide-container { 宽度:100%;高度:100%;溢出-x:滚动;溢出-y:隐藏;空白:nowrap;左填充:10%;填充权:5%; } .slide { 位置:相对;显示:内联块;高度:30%;边框:1px 纯绿色; } .slide-sizer { 高度:160%;指针事件:无; //border: 1px 纯红色; } . 幻灯片内容 { 位置:绝对;前10名%;左:10%; }
这个示例代码是一个简单的基于样式的解决方案根据动态高度保持元素的纵横比。当您增加和减少窗口高度时,元素应该跟随并且宽度应该依次跟随以保持纵横比。您会注意到,在 OSX 上的 Chrome 中(至少),“非强制”元素没有保持适当的比例。
如果您想保留在样式表中声明的样式,最好使用以下内容:
jQuery.fn.redraw = function() {
this.css('display', 'none');
var temp = this[0].offsetHeight;
this.css('display', '');
temp = this[0].offsetHeight;
};
$('.layer-to-repaint').redraw();
注意:对于最新的 webkit/blink,必须存储 offsetHeight
的值才能触发重绘,否则 VM(出于优化目的)可能会跳过该指令。
更新:添加了第二个 offsetHeight
读数,有必要通过恢复 display
值来防止浏览器排队/缓存以下 CSS 属性/类更改(这样应该呈现可以跟随的 CSS 转换)
仅限 CSS。这适用于删除或添加子元素的情况。在这些情况下,边框和圆角可能会留下伪影。
el:after { content: " "; }
el:before { content: " "; }
function resizeWindow(){
var evt = document.createEvent('UIEvents');
evt.initUIEvent('resize', true, false,window,0);
window.dispatchEvent(evt);
}
500 毫秒后调用此函数。
下面的 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 年 10 月,Chrome 85 的问题仍然存在。
morewry 使用 transform:translateZ(0) 的解决方案实际上可以使用任何转换,包括 translate(0,0) 和 scale(1),但是如果您必须再次更新元素,那么诀窍就是切换转换,并且最好的方法是在一帧后使用 requestAnimationFrame 直接删除它(应始终避免使用 setTimeout,因为它会更慢,因此可能会导致故障)。
所以,更新一个元素:
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');
});
}
20ms 后这对我有用:
const forceRedraw = elem => {
const t = elem.ownerDocument.createTextNode(' ') ;
elem.appendChild(t) ;
setTimeout(() => { elem.removeChild(t) }, 0) ;
}
一种在 IE 上对我有用的方法(我不能使用显示技术,因为有一个输入不能失去焦点)
如果您的边距为 0,它就可以工作(更改填充也可以)
if(div.style.marginLeft == '0px'){
div.style.marginLeft = '';
div.style.marginRight = '0px';
} else {
div.style.marginLeft = '0px';
div.style.marginRight = '';
}
示例 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();
.fadeIn(1)
而不是 .show(300)
对我来说 - 不要推动元素。所以我猜,这是触发 display:none
并返回 block
我对 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();
},
大多数答案需要使用异步超时,这会导致令人讨厌的眨眼。
但我想出了这个,它运行顺利,因为它是同步的:
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>
我想将所有状态返回到以前的状态(不重新加载),包括 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()">
我遇到了类似的问题,这行简单的 JS 有助于解决它:
document.getElementsByTagName('body')[0].focus();
就我而言,这是一个 Chrome 扩展程序在从扩展程序中更改其 CSS 后未重绘页面的错误。
我有一个反应组件列表,当滚动时,然后打开另一个页面,然后返回时,列表不会在 Safari iOS 上呈现,直到页面滚动。所以这就是修复。
componentDidMount() {
setTimeout(() => {
window.scrollBy(0, 0);
}, 300);
}
不定期副业成功案例分享
.hide().show(0)
(我选择了页脚),它应该刷新页面。