265

I have some trivial JavaScript to effect a style change:

sel = document.getElementById('my_id');
sel.className = sel.className.replace(/item-[1-9]-selected/,'item-1-selected');
return false;

This works fine with the latest versions of FF, Opera and IE, but fails on the latest versions of Chrome and Safari.

It affects two descendants, which happen to be siblings. The first sibling updates, but the second doesn’t. A child of the second element also has focus and contains the <a> tag that contains the above code in an onclick attribute.

In the Chrome “Developer Tools” window if I nudge (e.g. uncheck & check) any attribute of any element, the second sibling updates to the correct style.

Is there a workaround to easily and programmatically “nudge” WebKit into doing the right thing?

danorton
  • 11,804
  • 7
  • 44
  • 52
  • I think I'm experiencing this problem on Android 4.2.2 (Samsung I9500) when wrapping my canvas app in a WebView. Ridiculous! – Joshua Aug 05 '13 at 11:18
  • 6
    [**what-forces-layout.md**](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) a very good reading place – vsync Jan 24 '17 at 21:49

33 Answers33

332

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!

Jack
  • 9,448
  • 3
  • 29
  • 33
danorton
  • 11,804
  • 7
  • 44
  • 52
  • Curiously, this seems to set a different “dirty” bit than the one that caused the first sibling to update properly. This fragment makes everything repaint even if I put it _before_ the original fragment! – danorton Aug 15 '10 at 14:05
  • 33
    To avoid flickering you may try `'inline-block'`, `'table'` or [`'run-in'`](http://www.quirksmode.org/css/display.html#runin) instead of `'none'`, but this may have side-effects. Also, a timeout of 0 triggers a reflow just like querying `offsetHeight` does: `sel.style.display = 'run-in'; setTimeout(function () { sel.style.display = 'block'; }, 0);` – user123444555621 Feb 09 '12 at 13:59
  • We work in GWT and use just sel.style.display='block'; which did the trick. – Dr. Max Völkel Jun 22 '12 at 15:45
  • 15
    This answer is still useful 2 years later. I just used it to fix a Chrome-only issue where CSS box shadows remained on the screen after resizing the browser in certain ways. Thanks! – rkulla Aug 22 '12 at 17:32
  • Now, 22 november 2012, this also solves my CSS 3D problem on chrome on terminal server clients where it would only render perspective properly after a redraw. LOVE the code and it's still actual. – Tschallacka Nov 22 '12 at 10:56
  • Yes, this worked for a Safari 5 on OS X issue - thank you very much – James Westgate Jan 07 '13 at 10:49
  • This is the only answer I've found so far that works. I was trying to remove a css3 transition applied to the left property, change the left position, and then re-apply the css3 transition so that future changes to left would be animated. Javascript was being too efficient and only applied the final css change. So basically, this snippet forced the browser to accept the first change, pause, then accept the second change. – mrbinky3000 Mar 13 '13 at 16:00
  • 5
    @danorton You answered in 2010. It's 2013 and the bug is still around. Thanks. By the way, anyone know if there is any issue registered at webkit tracker? – RaphaelDDL Apr 24 '13 at 17:35
  • 1
    i can't believe this is (still) a bug. I mean... 10 years of web-dev and this is the first time I experience this one. – ProblemsOfSumit Aug 05 '13 at 12:56
  • 13
    PS.: for me the above solution didn't work anymore. Instead i used this (jQuery): sel.hide().show(0); Source: http://stackoverflow.com/questions/8840580/force-dom-redraw-refresh-on-chrome-mac – ProblemsOfSumit Aug 05 '13 at 13:45
  • 8
    All you need to do is read a size property, you dont need to change it back and forth. – Andrew Bullock Aug 06 '13 at 13:57
  • Solved my problem with reinitializing the table render after resizing the window. Awesome. And on a sidenote, wtf webkit browsers? – ravb79 Nov 11 '13 at 20:57
  • Thank you so much. The Nintendo 3DS's browser uses an old version of WebKit and it wasn't redrawing the canvas. Doing this after each draw fixed everything! – Andrea Dec 27 '13 at 14:00
  • Still a bug. Pops up when doing an Angularjs fill of a floated element. – Eric_WVGG Feb 24 '14 at 17:03
  • Thanks for this fix! In my case, was a Retina Mac/Chrome-only edge case with an `Ember.Input`. Doesn't even flicker! – bosgood Mar 17 '14 at 18:54
  • 2
    Changing the visibility would probably be better than changing the display property. – Jack Sep 26 '14 at 16:31
  • @Jack, it might be better if it works. It might also depend on exactly what style change needs triggering. – danorton Sep 26 '14 at 20:03
  • 1
    This does not work for me on Chrome or FF, however simply accessing `sel.offsetHeight` (without touching the display property) **does** work. Seems that the browser is skipping the calculation of offsetWidth/offsetHeight when the element is hidden.. – Grodriguez Oct 22 '14 at 10:03
  • This does not work for me in Chrome for mac. My case is applying `transition` and then change `transform` and `opacity`. The only solution I have found is to wait 100 milliseconds and then apply `opacity`. – Dan Nov 23 '14 at 19:10
  • 2
    Still a bug. Reading the offsetHeight by itself did not fix the problem for me and changing the display property was impractical so I used the visibility property and it worked! I placed the following in my jQuery.animate() callback function: this.style.visibility = "hidden"; this.offsetHeight; this.style.visibility = "visible"; I needed all 3 lines to make it work. The animation is still not visible all the time but when it's finished it will show up in the right place rather than partially somewhere else or not at all. – dev_willis Dec 10 '14 at 15:36
  • Incidentally, it had to be done on the specific positioned element that I wanted to fix. Applying this to the container that I was actually animating did not help. – dev_willis Dec 10 '14 at 15:39
  • 4
    It's 2015 by now and this answer is still relevant. Took me two hours to find. Thanks! – stiller_leser Jan 22 '15 at 09:29
  • 1
    If this hack doesn't work for you, you may need to just travel up the DOM tree and affect that element until it does. I tried every solution offered on this page and this is the only one that works, albeit not on the element affected itself. – Kevin Beal Jun 10 '15 at 16:52
  • Anyone know if there is an open issue with chrome or Webkit on this? – Sam Plus Plus Sep 08 '15 at 21:50
  • 1
    Amazingly this bug / fix still applies in 2016 - Javafx webview which uses webkit rendering engine. The issue i ran into was that on selecting checkboxes that were embedded in an svg, the check mark does not appear / go away. if you resize the window, it redraws and paints the correct state of the checkbox. – jayaram S Jan 14 '16 at 13:34
  • Happened on FF43, maybe due to my framework. But that solotion worked (i use jquery version: hide, offset, show) – teddy Jul 01 '16 at 04:52
  • Content that is dynamically generated into a container with flex-direction: column-reverse; applied is horribly unreliable on mobile devices about repainting children. Accessing offsetHeight, while toggling visibility seems to have cleared up most of the issues I was having. – relic Aug 09 '16 at 21:21
  • 2
    This solved my issue of Chrome not properly updating children's width and height inside of block which was being resized by JavaScript. The element was using CSS calc and Viewport units. I used sel.style.display='table'; sel.offsetHeight; sel.style.display='block'; (initial state was block). – Ignas2526 Feb 19 '17 at 06:02
  • I found this to be a non-starter because of flashing and the fact that it only seems to work with a subset of issues that pause app/ui painting. – Ben West May 15 '17 at 23:55
  • If, like me, your linter complaints about the second line, disable it on this particular line with a line comment: "// eslint-disable-line" for ESLint or "// jshint ignore:line" for JSHint – Lionel Gaillard Dec 21 '17 at 14:19
  • This solved a problem I was having where I had a bunch of absolutely positioned divs on top of an image. Each div has a different background color. For some reason they wouldn't update on Safari or IE. This solution fixes both browsers. Thanks so much! – Andrew Jul 26 '18 at 03:18
  • 1
    2020 and this bug still exists but your solution took care of it a decade later. – Johann Nov 20 '20 at 11:47
  • 2021 and this still works! BTW setting `display` to `none` is just fine, because the DOM won't be updated before the current call stack execution ends. – hudidit Jan 14 '21 at 13:59
  • This did the trick for me too, I had the issue on safari where overflow did not repaint correctly after a class toggle. Using `sel.style.display='table'` instead of `sel.style.display='none'` allowed to keep other css transitions working, otherwise it would just jump from one state to another. – Max Jul 17 '21 at 22:56
109

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.

morewry
  • 4,320
  • 3
  • 35
  • 35
  • 1
    Worked for me when using [angular-carousel](https://github.com/revolunet/angular-carousel)! – gustavohenke Oct 11 '14 at 04:20
  • 1
    Fixed my issue where border-radius was lost in an element inside a sliding panel – Timo Oct 28 '15 at 17:03
  • 1
    Works for cordova projects as well! – Quispie Apr 22 '16 at 08:11
  • 1
    Worked for me for a -webkit-line-clamp that didn't refresh – Adam Marshall Jul 13 '16 at 10:40
  • 1
    Worked for me on ipad(6.0) with -webkit-transform: translate(-50%, -50%). – Lain Nov 30 '16 at 09:51
  • 1
    This was the solution I needed to fix iOS Webkit repainting issues in a scrolling full screen [Reveal Modal](http://foundation.zurb.com/sites/docs/v/5.5.3/components/reveal.html). – fantasticrice Feb 10 '17 at 22:05
  • 1
    had to use JS to trigger this:`$this.css("transform", ""); setTimeout(function() { $this.css("transform","translateZ(0)"); }, 30);` – Bob S May 11 '17 at 10:41
  • 1
    Thanks, finally something that worked in my case with a `position: absolute;` div that is hidden with `display: none;` – cjohansson Jun 16 '17 at 13:36
  • 1
    I prefer this method to danorton's method due to its simplicity. It also worked for me when needing to refresh webkit after moving an absolutely-positioned div with text inside in javascript. However, I did it inside javascript like `objThatDoesNotUpdate.style.transform = "translateZ(0)";` :) I guess you could also do `objThatDoesNotUpdate.classList.add("willNotRender"); objThatDoesNotUpdate.classList.remove("willNotRender");` (not tested) – dylnmc Jan 09 '18 at 21:25
  • 1
    thanks!!!! I was wasting my time because of a webkit bug (firefox works perfectly) caused by a background-image trick (to only apply opacity to background rather than the whole div content) – Oliboy50 May 10 '18 at 18:39
  • 1
    This worked for Ionic Segment change. Apply the class to a wrapper div INSIDE tag. – Low Sep 03 '18 at 06:16
  • 1
    This worked for me with a conditional div under Angular 6 not rendering once shown. I appreciate it. – Jeph Mar 14 '19 at 19:38
  • 3
    Original question was asked in 2010. This answer was written in 2014 and is a great solution. It's 2019 and this is still an issue (albeit only in Safari)... Well played, WebKit. – CWSpear Mar 26 '19 at 02:54
  • 3
    safari sucks, the new ie6 – Nikos Sep 02 '20 at 07:33
  • Note that `will-update` is available in Safari > 9 – ptim Jul 13 '21 at 07:22
  • Thankyou !! I'm almost tempted to but a `translateZ(0)` on everything just to avoid these endless safari bugs – The Sloth Oct 06 '21 at 13:16
50

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.

Gerben
  • 16,747
  • 6
  • 37
  • 56
  • 3
    I'd upvote 1,000,000 times if I could. This is the only way I could get chrome to repaint a stupid div I was dragging around. By the way, you don't have to add a style element (at least I didn't) any element will work. I made a div and gave it an id so I could delete then add the element on each step, so as not to fill the DOM up with useless tags. – hobberwickey Dec 12 '12 at 00:03
  • 6
    just used this mixed with Pumbaa80's comment on another answer. The fix I ended up with was `var div = $('
    ').appendTo(element); setTimeout(function(){ div.remove(); }, 0);`
    – bendman Jan 08 '13 at 16:11
  • 33
    This single line is working great for me ! `$('').appendTo($(document.body)).remove();` – pdelanauze Feb 21 '13 at 14:50
  • 1
    @pdelanauze answer works perfect. I have a redraw problem sing css3 translate and transform in ios. – Marcel Falliere May 30 '13 at 21:18
  • 12
    Just know that this forces a repaint of the entire page, whereas most of the time you only want to repaint a certain section of the page... – Ryan Wheale Aug 06 '13 at 22:40
  • @Web_Designer: Are you aware that the code given in your comment is actually invalid? If you think it is valid, paste it into the web console and run it. – user21820 Nov 10 '17 at 07:57
35

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)';
EricG
  • 3,788
  • 1
  • 23
  • 34
  • 3
    I used this in a hack I was trying, but instead of `scale(1)`, I assigned it to itself as `element.style.webkitTransform = element.style.webkitTransform`. The reason for this being that setting it to the former was distorting the page slightly for `absolute`ly positioned elements! – bPratik Apr 30 '12 at 00:26
  • This worked for me, and didn't have problems flickering or resetting the scroll position that the `display` solution did. – davidtbernal Jul 24 '14 at 16:54
  • I had a Cordova app using a lot of dynamic SVG. Worked fine everywhere except iOS7, where if just wouldn't display the SVG elements on startup. Added a simple $('body')[0].style.webkitTransform = 'scale(1)'; to the initialisation code and the problem disappeared! – Herc May 23 '16 at 13:57
  • This is not working as of right now, on the newest Safari and iOS. – setholopolus Aug 31 '18 at 16:41
  • @setholopolus which versoin(s) would that be? – EricG Sep 03 '18 at 12:13
  • iOS 11+. I have Safari 11.1.2, I'm not sure about other versions of Safari. – setholopolus Sep 04 '18 at 18:12
  • holly cow ! 8 hours to find why my iframe content appears wrongly (element present but with no opacity ??) ... this fixed, it. Be careful the redrawing is done only if the css value is changing, calling this twice will do only one redraw. – Daphoque Jan 06 '21 at 13:20
25

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.

Rob Murphy
  • 933
  • 8
  • 14
16

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; }
sweeds
  • 539
  • 6
  • 18
  • Looks like it's working for me too. Used this to fix a rendering issue with Mobile Safari – Chris Mar 07 '16 at 16:20
  • This fixes my issue, forces safari to relayout. However, I used `zoom` instead of padding: `@-webkit-keyframes safariBugFix {from { zoom: 100%; } to { zoom: 99.99999%; }}` – Ahmed Musallam Jul 10 '17 at 22:58
14

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.

sowasred2012
  • 695
  • 1
  • 7
  • 22
  • 7
    I tried everything above this, but only this one worked for me. WTF webkit! This is not computer science! This is black magic! – Charlie Martin Jun 12 '14 at 20:47
7

I came up here because I needed to redraw scrollbars in Chrome after changing its css.

If someone's having the same problem, I solved it by calling this function:

//Hack to force scroll redraw
function scrollReDraw() {
    $('body').css('overflow', 'hidden').height();
    $('body').css('overflow', 'auto');
}

This method is not the best solution, but it may work with everything, hiding and showing the element that needs to be redraw may solve every problem.

Here is the fiddle where I used it: http://jsfiddle.net/promatik/wZwJz/18/

António Almeida
  • 9,620
  • 8
  • 59
  • 66
  • 1
    Great! I was trying to animate the scrollbars too and that is what I needed! Btw better use overflow: overlay for animated scrollbars – ekalchev Oct 08 '19 at 15:24
6

I stumbled upon this today: Element.redraw() for prototype.js

Using:

Element.addMethods({
  redraw: function(element){
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    (function(){n.parentNode.removeChild(n)}).defer();
    return element;
  }
});

However, I've noticed sometimes that you must call redraw() on the problematic element directly. Sometimes redrawing the parent element won't solve the problem the child is experiencing.

Good article about the way browsers render elements: Rendering: repaint, reflow/relayout, restyle

Adam Eberlin
  • 14,005
  • 5
  • 37
  • 49
4

I had this problem with a a number of divs that were inserted in another div with position: absolute, the inserted divs had no position attribute. When I changed this to position:relative it worked fine. (was really hard to pinpoint the problem)

In my case the elements where inserted by Angular with ng-repeat.

justGoscha
  • 24,085
  • 15
  • 50
  • 61
  • 1
    Thanks! This worked for my issue as well, and is a lot more light weight that a lot of the javascript solutions above. – Dsyko Aug 22 '14 at 00:37
4

I cannot believe this is still a problem in 2014. I just had this issue when refreshing a fixed position caption box on the lower-left hand of the page while scrolling, the caption would 'ghost' its way up the screen. After trying everything above without success, I noticed a lot of things were either slow/causing issues due to creating very short DOM relayouts etc causing somewhat unnatural feeling scrolling etc...

I ended up making a fixed position, full-size div with pointer-events: none and applying danorton's answer to that element, which seems to force a redraw on the whole screen without interfering with the DOM.

HTML:

<div id="redraw-fix"></div>

CSS:

div#redraw-fix {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 25;
    pointer-events: none;
    display: block;
}

JS:

sel = document.getElementById('redraw-fix');
sel.style.display='none';
sel.offsetHeight; // no need to store this anywhere, the reference is enough
sel.style.display='block';
waffl
  • 5,179
  • 10
  • 73
  • 123
  • I cannot thank you enough for your workaround which is the only one that worked for me. Yes, it's 2017 and the issue lingers on. My problem was related to an RTL language page which would render blank if manually refreshed on Android mobile devices. The `z-index` and `pointer-events` rules are superfluous here. – Amin Mozafari Apr 14 '17 at 11:00
  • I've used danorton's fix before but was really struggling with the flash of content from setting display to none. Your solution worked out great for me with no content flash! – Justin Noel Jul 20 '18 at 19:11
  • try 2020 lol, will it help me? https://stackoverflow.com/questions/63690664/safari-macos-foreign-object-not-scaled-properly-inside-svg?noredirect=1#comment112637717_63690664 – Nikos Sep 02 '20 at 07:34
4

I use the transform: translateZ(0); method but in some cases it is not sufficient.

I'm not fan of adding and removing a class so i tried to find way to solve this and ended up with a new hack that works well :

@keyframes redraw{
    0% {opacity: 1;}
    100% {opacity: .99;}
}

// ios redraw fix
animation: redraw 1s linear infinite;
3

Not that this question needs another answer, but I found simply changing the color by a single bit forced a repaint in my particular situation.

//Assuming black is the starting color, we tweak it by a single bit
elem.style.color = '#000001';

//Change back to black
setTimeout(function() {
    elem.style.color = '#000000';
}, 0);

The setTimeout proved critical to move the second style change outside the current event loop.

TMan
  • 1,775
  • 1
  • 14
  • 26
  • 1
    Setting a timeout of 0 proved useful for me in a different but similar situation. The redraw wasn't occuring before jquery unobtrusive validation froze up the screen while it parsed a large form. Thought I'd comment in case someone out there is in the same situation. The other solutions did not work in this scenario. – k29 Aug 24 '15 at 15:36
2

The only solution works for me is similar to sowasred2012's answer:

$('body').css('display', 'table').height();
$('body').css('display', 'block');

I have a lot of problem blocks on page, so I change display property of root element. And I use display: table; instead of display: none;, because none will reset scrolling offset.

Ilya Sviridenko
  • 394
  • 3
  • 6
1

Since everyone seems to have their own problems and solutions, I figured I'd add something that works for me. On Android 4.1 with current Chrome, trying to drag a canvas around inside a div with overflow:hidden, I couldn't get a redraw unless I added an element to the parent div (where it wouldn't do any harm).

var parelt = document.getElementById("parentid");
var remElt = document.getElementById("removeMe");
var addElt = document.createElement("div");
addElt.innerHTML = " "; // Won't work if empty
addElt.id="removeMe";
if (remElt) {
    parelt.replaceChild(addElt, remElt);
} else {
    parelt.appendChild(addElt);
}

No screen flicker or real update, and cleaning up after myself. No global or class scoped variables, just locals. Doesn't seem to hurt anything on Mobile Safari/iPad or desktop browsers.

Eric Ruck
  • 671
  • 6
  • 6
1

I am working on ionic html5 app, on few screens i have absolute positioned element, when scroll up or down in IOS devices (iPhone 4,5,6, 6+)i had repaint bug.

Tried many solution none of them was working except this one solve my problem.

I have use css class .fixRepaint on those absolute positions elements

.fixRepaint{
    transform: translateZ(0);
}

This has fixed my problem, it may be help some one

Anjum....
  • 4,086
  • 1
  • 35
  • 45
1

This answer is for those, who is struggling with @danorton's solution using Angular 10. This solution

sel.style.display='none';
sel.offsetHeight;
sel.style.display='';

works for me only with "buildOptimizer": false in angular.json file. Looks like optimizer breaks this somehow.

Dharman
  • 30,962
  • 25
  • 85
  • 135
AntGrisha
  • 409
  • 1
  • 3
  • 13
0

This is fine for JS

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

But in Jquery, and particularly when you can only use $(document).ready and cannot bind to a the .load event of an object for any particular reason, the following will work.

You need to get the OUTER(MOST) container of the objects/divs and then remove all its contents into a variable, then re-add it. It will make ALL changes done within the outer container visible.

$(document).ready(function(){
    applyStyling(object);
    var node = $("div#body div.centerContainer form div.centerHorizontal").parent().parent();
    var content = node.html();
    node.html("");
    node.html(content);
}
WiR3D
  • 1,465
  • 20
  • 23
0

I've found this method to be useful when working with transitions

$element[0].style.display = 'table'; 
$element[0].offsetWidth; // force reflow
$element.one($.support.transition.end, function () { 
    $element[0].style.display = 'block'; 
});
jschr
  • 1,866
  • 2
  • 16
  • 21
0

the "display/offsetHeight" hack didn't work in my case, at least when it was applied to the element being animated.

i had a dropdown menu that was being open/closed over the page content. the artifacts were being left on the page content after the menu had closed (only in webkit browsers). the only way the "display/offsetHeight" hack worked is if i applied it to the body, which seems nasty.

however, i did find another solution:

  1. before the element starts animating, add a class that defines "-webkit-backface-visibility: hidden;" on the element (you could also use inline style, i'd guess)
  2. when it's done animating, remove the class (or style)

this is still pretty hacky (it uses a CSS3 property to force hardware rendering), but at least it only affects the element in question, and worked for me on both safari and chrome on PC and Mac.

0

This seems related to this: jQuery style not being applied in Safari

The solution suggested in the first response has worked well for me in these scenarios, namely: apply and remove a dummy class to the body after making the styling changes:

$('body').addClass('dummyclass').removeClass('dummyclass');

This forces safari to redraw.

Community
  • 1
  • 1
steve
  • 1,335
  • 1
  • 11
  • 8
  • To get this to work in Chrome I had to add: $('body').addClass('dummyclass').delay(0).removeClass('dummyclass'); – mbokil Mar 15 '14 at 06:11
0

above suggestions didnt work for me. but the below one does.

Want to change the text inside the anchor dynamically. The word "Search". Created an inner tag "font" with an id attribute. Managed the contents using javascript (below)

Search

script contents:

    var searchText = "Search";
    var editSearchText = "Edit Search";
    var currentSearchText = searchText;

    function doSearch() {
        if (currentSearchText == searchText) {
            $('#pSearch').panel('close');
            currentSearchText = editSearchText;
        } else if (currentSearchText == editSearchText) {
            $('#pSearch').panel('open');
            currentSearchText = searchText;
        }
        $('#searchtxt').text(currentSearchText);
    }
ganesh
  • 1
0

I was having an issue with an SVG that was disappearing on Chrome for Android when the orientation was changed in certain circumstances. The below code doesn't reproduce it, but is the setup we had.

body {
  font-family: tahoma, sans-serif;
  font-size: 12px;
  margin: 10px;
}
article {
  display: flex;
}
aside {
  flex: 0 1 10px;
  margin-right: 10px;
  min-width: 10px;
  position: relative;
}
svg {
  bottom: 0;
  left: 0;
  position: absolute;
  right: 0;
  top: 0;
}
.backgroundStop1 {
  stop-color: #5bb79e;
}
.backgroundStop2 {
  stop-color: #ddcb3f;
}
.backgroundStop3 {
  stop-color: #cf6b19;
}
<article>
  <aside>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="100%" width="100%">
      <defs>
        <linearGradient id="IndicatorColourPattern" x1="0" x2="0" y1="0" y2="1">
          <stop class="backgroundStop1" offset="0%"></stop>
          <stop class="backgroundStop2" offset="50%"></stop>
          <stop class="backgroundStop3" offset="100%"></stop>
        </linearGradient>
      </defs>
      <rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" fill="url(#IndicatorColourPattern)"></rect>
    </svg>
  </aside>
  <section>
    <p>Donec et eros nibh. Nullam porta, elit ut sagittis pulvinar, lacus augue lobortis mauris, sed sollicitudin elit orci non massa. Proin condimentum in nibh sed vestibulum. Donec accumsan fringilla est, porttitor vestibulum dolor ornare id. Sed elementum
      urna sollicitudin commodo ultricies. Curabitur tristique orci et ligula interdum, eu condimentum metus eleifend. Nam libero augue, pharetra at maximus in, pellentesque imperdiet orci.</p>
    <p>Fusce commodo ullamcorper ullamcorper. Etiam eget pellentesque quam, id sodales erat. Vestibulum risus magna, efficitur sed nisl et, rutrum consectetur odio. Sed at lorem non ligula consequat tempus vel nec risus.</p>
  </section>
</article>

Day and half later after poking and prodding and not happy with the hacky solutions offered here, I discovered that the issue was caused by the fact it seemed to keep the element in memory while drawing a new one. The solution was to make the ID of the linearGradient on the SVG unique, even though it was only ever used once per page.

This can be achieved many different ways, but for our angular app we used lodash uniqueId function to add a variable to the scope:

Angular Directive (JS):

scope.indicatorColourPatternId = _.uniqueId('IndicatorColourPattern');

HTML Updates:

Line 5: <linearGradient ng-attr-id="{{indicatorColourPatternId}}" x1="0" x2="0" y1="0" y2="1">

Line 11: <rect x="0" y="0" rx="5" ry="5" width="100%" height="100%" ng-attr-fill="url(#{{indicatorColourPatternId}})"/>

I hope this answer saves someone else a days worth of face-smashing their keyboard.

Jamie Barker
  • 8,145
  • 3
  • 29
  • 64
0

I would recommend a less hackish and more formal way to force a reflow: use forceDOMReflowJS. In your case, your code would look as follows.

sel = document.getElementById('my_id');
forceReflowJS( sel );
return false;
Jack G
  • 4,553
  • 2
  • 41
  • 50
0

I found that just adding a content style to the element forced it to repaint, this should be a different value every time you want it to redraw and doesn't need to be on a pseudo element.

.selector {
    content: '1'
}
Steve
  • 4,314
  • 3
  • 16
  • 21
0

I tried morewry answer but it does not work for me. I had trouble to have the same clientWidth with safari comparing to others browsers and this code solved the problem:

var get_safe_value = function(elm,callback){
    var sty = elm.style
    sty.transform = "translateZ(1px)";
    var ret = callback(elm)//you can get here the value you want
    sty.transform = "";
    return ret
}
// for safari to have the good clientWidth
var $fBody = document.body //the element you need to fix
var clientW = get_safe_value($fBody,function(elm){return $fBody.clientWidth})

It is really strange because if I try again to get the clientWidth after get_safe_value, I obtain a bad value with safari, the getter has to be between sty.transform = "translateZ(1px)"; and sty.transform = "";

The other solution that works definitively is

var $fBody = document.body //the element you need to fix
$fBody.style.display = 'none';
var temp = $.body.offsetHeight;
$fBody.style.display = ""
temp = $.body.offsetHeight;

var clientW = $fBody.clientWidth

The problem is that you lose focus and scroll states.

bormat
  • 1,309
  • 12
  • 16
0

This code will rerender css

 document.body.style.display = 'flex';
 setTimeout(() => (document.body.style.display = ''), 0);
Nagibaba
  • 4,218
  • 1
  • 35
  • 43
0

Setting the transform CSS to scale(0.9999) apparently works in the newest chrome.

function redraw(node){
     // Adjust the 200 as fastest as you can
     // or, change the setTimeout to requestAnimationFrame as soon as the element
     // is drawn
     setTimeout(() => (node.style.transform = "scale(0.9999)"), 200);
}
Ardika Rifqi
  • 11
  • 1
  • 2
0

I found that my issue was resolved by @morewry's excellent answer, and furthermore that the will-change property arrived since.

CSS will-change or another succeeding specification will be the future solution, most likely.

In my case, the value of will-change: transform; alone was effective in Safari 14.

.wont-update {
    will-change: transform;

    /* for Safari < 9.1 */
    transform: translateZ(0);
}
ptim
  • 14,902
  • 10
  • 83
  • 103
0

I also faced the same issue working with Angular 10. I tried many solutions but nothing seems to be working. The fix which worked for me is handling it through JS. I just removed the element from DOM and added again using ngIf.

HTML:

<div *ngIf="showElement">
  Contents of your element
</div>

JS:

this.showElement = false;
setTimeout(() => {
  this.showElement = true;
}, 10);
0

I would like to add that the style now needs to be set on the parent node.

 sel.parentNode.style.display = 'none';
 sel.parentNode.offsetHeight;
 sel.parentNode.style.display = '';

This is the only solution that worked for me in the current version of Safari (15.6).

Ood
  • 1,445
  • 4
  • 23
  • 43
-1

A simple solution with jquery:

$el.html($el.html());

or

element.innerHTML = element.innerHTML;

Had an SVG that wasn't showing when it was added to the html.

This can be added after the svg elements are on the screen.

Better solution is to use:

document.createElementNS('http://www.w3.org/2000/svg', 'svg');

and with jQuery:

$(svgDiv).append($(document.createElementNS('http://www.w3.org/2000/svg', 'g'));

this will render correctly on Chrome.

Sumurai8
  • 20,333
  • 11
  • 66
  • 100
  • The problem with this approach is that you loose any event handlers you have registered on the object or its descendants. – Haqa Aug 18 '16 at 13:49
-1

This will force repaint while avoid flickering, existing element tinkering and any layout issue...

function forceRepaint() {
    requestAnimationFrame(()=>{
      const e=document.createElement('DIV');
      e.style='position:fixed;top:0;left:0;bottom:0;right:0;background:#80808001;\
               pointer-events:none;z-index:9999999';
      document.body.appendChild(e);
      requestAnimationFrame(()=>e.remove());  
    });
}
Athelstone
  • 59
  • 1
  • 3