[Implementersâ doc]
This document discusses the CSS specs implementers can use in case they have performance issues:
will-change
;At first sight, using those 2 specs can give tremendous results but you sometimes have to use them in clever ways: itâs not just about adding a line of CSS in your stylesheet.
Set will-change to the properties youâll actually change, on the elements that are actually changing. And remove it when they stop.
The will-change
property allows an author to inform the UA ahead of time of what kinds of changes they are likely to make to an element. The UA can then try to optimize how they handle the element ahead of time, performing potentially-expensive work preparing for an animation before the animation actually begins.
In other words, will-change
is a UA hint, it doesnât have any styling effect on the elements for which youâre using it. Itâs worth noting it can have appearance effects though, if a new stacking context is created.
It is, in many ways, an official property for the infamous âtranslate-z hack.â Although it was meant for animation, it can also deal with more simple changes.
will-change: <animateable-feature> = scroll-position || contents || <custom-ident>
Indicates that the author expects to animate or change the scroll position of the element in the near future.
A browser might take this value as a signal to expand the range of content around the scroll window that is rendered, so that longer/faster scrolls can be done smoothly.
Indicates that the author expects to animate or change something about the elementâs contents in the near future.
A browser might take this value as a signal to cache less aggressively on the element, or avoid caching at all and just continually re-render the element from scratch.
Indicates that the author expects to animate or change the property with the given name on the element in the near future.
A browser might take a value of transform as a signal that it should go ahead and promote the element to its own layer immediately, before the element starts to be transformed, to avoid any delay involved in rerendering the old and new layers.
By using will-change
, youâre basically telling the browser the element is a few moments away from changing, the browser consequently dedicates memory and resources to this element. If you use it for too many elements, it will thus degrade performance.
This property should be considered a last resort, it was not meant for premature optimization. You should use it only if you have to deal with performance issues.
will-change
sparingly in stylesheetswill-change
sufficient time to work<custom-ident>
to target super specific changes (left
, opacity
, etc.)will-change
after the changes are doneSpecifying will-change
for a small number of persistent UI elements in a page which should react snappily to the user is appropriate:
body > .sidebar {
will-change: transform;
/* Will use 'transform' to slide it out when the user requests. */
}
Because this is limited to a small number of elements, the fact that the optimization is rarely actually used doesnât hurt very much.
If an element is going to change when a user clicks on it, setting will-change
on hover will usually give at least 200 milliseconds for the optimizations to be set up, as human reaction time is relatively slow.
.element {
transition: opacity 0.2s;
opacity: 1;
}
.element:hover {
will-change: opacity;
}
.element:active {
opacity: 0.3;
}
This is probably the way to go, should you try using will-change
to optimize performance.
Here we have the hint added on :hover
, then removed when the animation has ended.
var el = document.getElementById('element');
// Set will-change when the element is hovered
el.addEventListener('mouseenter', hintBrowser);
el.addEventListener('animationEnd', removeHint);
function hintBrowser() {
// The optimizable properties that are going to change
// in the animation's keyframes block
this.style.willChange = 'transform, opacity';
}
function removeHint() {
this.style.willChange = 'auto';
}
Note: This is only implemented in Blink (Chrome + Opera). It is under consideration for Microsoft Edge.
The contain
property indicates that the element and its contents are, as much as possible, independent of the rest of the page.
This allows the browser to recalculate layout, style, paint, size, or any combination of them for a limited area of the DOM and not the entire page. To put it simply, it lets developers limit the scope of the browserâs styles, layout and paint work.
It was primarily meant for webpages which contain a lot of widgets that are all independent as it can be used to prevent one widgetâs CSS rules from changing other things on the page.
contain: strict || content || size || layout || style || paint
This is a shorthand for all values, it is equivalent to contain: size layout style paint
.
This is a shorthand for all values but size
, it is equivalent to contain: layout style paint
.
It indicates that the element can be laid out without the need to examine its descendants.
It indicates that nothing outside can affect the elementâs internal layout, and vice versa.
It indicates that, for properties which can have effects on more than just an element and its descendants, those effects donât escape the containing element.
It indicates that the descendants of the element donât display outside its bounds, so if an element is off-screen or otherwise not visible, its descendants are also guaranteed to be not visible.
In theory, huge performance gains are possible, but it requires a solid knowledge of how browsers work and what causes reflow, relayout, repaint, etc. Judging by examples, I can see how it could be useful but weâll probably have to find inventive and clever ways to leverage it since our scope is very often the root document.
If you need a refresher:
left
property, every single element in the DOM might need to be checked. Enabling containment here can potentially reduce the number of elements to just a handful, rather than the whole document, saving the browser a ton of unnecessary work and significantly improving performance.z-index
will have an effect on the element, and children will be stacked according to the new context.