How to force Safari to repaint position:fixed elements on scroll?
You're not crazy. I'm having issues with position: fixed
elements not repainting either. Haven't found a solution yet.
Edit Found a solution. You can make almost anything repaint by triggering a CSS animation on it that bumps it's size. Here's the snippet I'm using:
.foo
position: fixed
&.active
animation: repaint 1ms
@keyframes repaint
from
width: 99.999%
to
width: 100%
In case you have no idea what stylus styles are and thus can not use @CorySimmons' solution here is a css version of the code above. Also I had to change some values, clearly the values above those not work in iOS 8
@-webkit-keyframes repaint {
from {
width: 99.9%;
}
to {
width: 100%;
}
}
@-moz-keyframes repaint {
from {
width: 99.9%;
}
to {
width: 100%;
}
}
@keyframes repaint {
from {
width: 99.9%;
}
to {
width: 100%;
}
}
.repaint {
-webkit-animation: repaint 100ms;
-moz-animation: repaint 100ms;
-ms-animation: repaint 100ms;
animation: repaint 100ms;
}
All you need to do is to give the fixed element a class of .repaint when it needs to be repainted. In my case it was a sticky navigation using jQuery's scrollTop() to add and remove classes from my masthead, so when needed, the jquery function also added .repaint class to my masthead and it solved the issue for me.
I have encountered exactly the same problem in Safari 9.1.
Extending the time that animation executes worked for me in most cases.
@keyframes repaint {
from {
width: 99.999%
}
to {
width: 100%
}
}
.repaint {
animation: repaint 5000ms;
}
However if the fixed position DOM element was inside the parent that height changed (e.g. parent height can change when new DOM elements are dynamically added), then it didn't work for me, even when extending the animation time to unreasonable values.
My final solution was to drop the animation
hack and to force redraw in JS with
$('.repaint').hide().show(0);
as suggested in Force DOM redraw/refresh on Chrome/Mac
I use AngularJS, and to have this hack working in all cases I had to call this .hide().show(0)
on each digest loop.
Hack in the form of AngularJS directive:
function ForceRepaintDirective() {
return {
restrict: 'EA',
link: function(scope, $element) {
scope.$watch(function() {
$element.hide().show(0);
});
}
};
};