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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8840580/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 07:45:36  来源:igfitidea点击:

Force DOM redraw/refresh on Chrome/Mac

javascriptcssmacosdomgoogle-chrome

提问by Jason Kester

Every once in a while, Chrome will render perfectly valid HTML/CSS incorrectly or not at all. Digging in through the DOM inspector is often enough to get it to realize the error of its ways and redraw correctly, so it's provably the case that the markup is good. This happens frequently (and predictably) enough in a project I'm working on that I've put code in place to force a redraw in certain circumstances.

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

This works in most browser/os combinations:

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

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

As in, tweak some unused CSS property, then ask for some information that forces a redraw, then untweak the property. Unfortunately, the bright team behind Chrome for the Mac seem to have found a way to get that offsetHeight without redrawing. Thus killing an otherwise useful hack.

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

Thus far, the best I've come up with to get the same effect on Chrome/Mac is this piece of ugliness:

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

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

As in, actually force the element to jump a bit, then chill a second and jump it back. Making it worse, if you drop that timeout below 500ms (to where it would be less noticeable), it often won't have the desired effect, since the browser won't get around to redrawing before it goes back to its original state.

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

Anybody care to offer a better version of this redraw/refresh hack (preferably based on the first example above) that works on Chrome/Mac?

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

回答by Juank

Not sure exactly what you're trying to achieve but this is a method I have used in the past with success to force the browser to redraw, maybe it will work for you.

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

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

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

If this simple redraw doesn't work you can try this one. It inserts an empty text node into the element which guarantees a redraw.

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

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
}

EDIT: In response to ?ime Vidas what we are achieving here would be a forced reflow. You can find out more from the master himself http://paulirish.com/2011/dom-html5-css3-performance/

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

回答by Vincent Sels

None of the above answers worked for me. I did notice that resizingmy window did cause a redraw. So this did it for me:

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

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

回答by morewry

We recently encountered this and discovered that promoting the affected element to a composite layerwith translateZfixed the issue without needing extra javascript.

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

.willnotrender { 
   transform: translateZ(0); 
}

As these painting issues show up mostly in Webkit/Blink, and this fix mostly targets Webkit/Blink, it's preferable in some cases. Especially since many JavaScript solutions cause a reflowand repaint, not just a repaint.

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

回答by aviomaksim

This solution without timeouts! Real forceredraw! For Android and iOS.

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

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

回答by zupa

This works for me. Kudos go here.

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

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

$(el).redraw();

回答by Brady Emerson

Hiding an element and then showing it again within a setTimeout of 0 will force a redraw.

隐藏一个元素,然后在 setTimeout 为 0 内再次显示它会强制重绘。

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

回答by Xavier Follet

This seems to do the trick for me. Plus, it really doesn't show at all.

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

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

回答by qlqllu

call window.getComputedStyle()should force a reflow

调用window.getComputedStyle()应该强制重排

回答by Ben Feely

I ran into this challenge today in OSX El Capitan with Chrome v51. The page in question worked fine in Safari. I tried nearly every suggestion on this page - none worked right - all had side-effects... I ended up implementing the code below - super simple - no side-effects (still works as before in Safari).

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

Solution: Toggle a class on the problematic element as needed. Each toggle will force a redraw. (I used jQuery for convenience, but vanilla JavaScript should be no problem...)

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

jQuery Class Toggle

jQuery 类切换

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

CSS Class

CSS 类

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

And that's it...

就是这样......

NOTE: You have to run the snippet below "Full Page" in order to see the effect.

注意:您必须运行“整页”下方的代码段才能看到效果。

$(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>

回答by Shobhit

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

call this function after 500 milliseconds.

500 毫秒后调用此函数。