This is a tech post by Arnaud Breton, full-stack developer here at Mention with a specific focus on the front-end side and user experience. Before joining mention, Arnaud was co-founder and CTO of UniShared and VideoNot.es, part of the Imagine K-12 2013 Winter batch.
While redesigning mention’s website, we ran into a lot of slow rendering performance issues with Google Chrome (M33, released February 2014, also in the last canary version as of posting).
Our new homepage has a fixed background attached to a particular DIV, something that looks very classic on modern websites. Everything was going well, except on high density displays like Apple’s Retina. On these displays, the overall scroll was very laggy (see here), making the experience very unreliable.
As we started to look for some feedback about fixed backgrounds, we found nothing that mentioned the exact issue we were having. So we started to track down what was going on, leveraging the amazing tools that are bundled in Chrome.
One of these is the “Show painted rectangles” mode (open the Dev tools, press escape and go to the “Rendering” tab).
It displays all of the re-painted zones while scrolling. Using this mode, we discovered that the fixed background actually causes Chrome to always repaint the entire layout, even when the DIV was not displayed in the viewport.
Our first solution was to remove the
background-attachment:fixed property, which was causing the lag when the element was not displayed (using this simple function to detect if the element is currently displayed in the viewport).
It was working great on the part of the page where the background is invisible, but the laggy effect come back immediately when scrolling it into view.
Continuing to investigate, we found this blog post by Remy Sharp (who I actually met at dotJS in Paris last October where he was talking about iframes abuses) that mentioned our issue. The solution consists in applying the background to a fixed positioned DIV and using the
-webkit-transform: translateZ(0) property to force Chrome to put it in it’s own composited layer (more about this Chrome hack here).
In our case, the background is only visible on a defined part of the page. Playing with the background color (white in our case) and z-index properties on all of the other blocks except the one where the background has to be displayed, we managed to obtain the desired effect! Bazinga!
We published the code on Github to showcase the issue. You can directly experiment with the issue here. The final render is also available here.
It seems to only affect Chrome. Firefox and Safari are not impacted (so it is not a WebKit issue) since they already seem to put these kinds of DIV into their own layer.
Tracking down this kind of performance issue ensures the very best possible experience for your customers. It’s not always this easy, but fortunately modern browsers (and particularly Chrome) are bundled with a lot of useful tools like the one we used here. You can find a complete article about tools like these on HTML5Rocks (which I personally recommend for its great content).
Have you experienced this same issue? Did you find a different solution? Let us know, tweet us @mention, or at Arnaud directly @arnaud_breton.