Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I force WebKit to redraw/repaint to propagate style changes?

I found some complicated suggestions and many simple ones that didn’t work, but a comment to one of them by Vasil Dinkov provided a simple solution to force a redraw/repaint that works just fine:

sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='';

I’ll let someone else comment if it works for styles other than “block”.

Thanks, Vasil!


We recently encountered this and discovered that promoting the affected element to a composite layer with translateZ in CSS fixed the issue without needing extra 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 the accepted answer almost certainly causes a reflow and repaint, not just a repaint.

Webkit and Blink have been working hard on rendering performance, and these kinds of glitches are the unfortunate side effect of optimizations that aim to reduce unnecessary flows and paints. CSS will-change or another succeeding specification will be the future solution, most likely.

There are other ways to achieve a composite layer, but this is the most common.


danorton solution didn't work for me. I had some really weird problems where webkit wouldn't draw some elements at all; where text in inputs wasn't updated until onblur; and changing className would not result in a redraw.

My solution, I accidentally discovered, was to add a empty style element to the body, after the script.

<body>
...
<script>doSomethingThatWebkitWillMessUp();</script>
<style></style>
...

That fixed it. How weird is that? Hope this is helpful for someone.


Since the display + offset trigger didn't work for me, I found a solution here:

http://mir.aculo.us/2009/09/25/force-redraw-dom-technique-for-webkit-based-browsers/

i.e.

element.style.webkitTransform = 'scale(1)';

I was suffering the same issue. danorton's 'toggling display' fix did work for me when added to the step function of my animation but I was concerned about performance and I looked for other options.

In my circumstance the element which wasn't repainting was within an absolutely position element which did not, at the time, have a z-index. Adding a z-index to this element changed the behaviour of Chrome and repaints happened as expected -> animations became smooth.

I doubt that this is a panacea, I imagine it depends why Chrome has chosen not to redraw the element but I'm posting this specific solution here in the help it hopes someone.

Cheers, Rob

tl;dr >> Try adding a z-index to the element or a parent thereof.


The following works. It only has to be set once in pure CSS. And it works more reliably than a JS function. Performance seems unaffected.

@-webkit-keyframes androidBugfix {from { padding: 0; } to { padding: 0; }}
body { -webkit-animation: androidBugfix infinite 1s; }

For some reason I couldn't get danorton's answer to work, I could see what it was supposed to do so I tweaked it a little bit to this:

$('#foo').css('display', 'none').height();
$('#foo').css('display', 'block');

and it worked for me.