When Apple released iOS 5 in 2011, one of the improvements included in Mobile Safari was the inclusion of support for scrolling of block elements with the CSS overflow:auto
or overflow:scroll
declarations. In addition, support for fancy and fast bounce-scroll behavior is provided with the inclusion of the -webkit-overflow-scrolling: touch
vendor-specific declaration.
The inclusion of scrolling on clipped elements is great and goes a ways towards making it much easier to layout HTML5 pages and apps in a consistent manner, and closes the gap a bit between web and native behaviors. And it’s easy to implement.
For example, to add scrolling on an element with class=”scrolling-element”:
Fast Scrolling, Broken Rendering
Unfortunately, Apple released a buggy implementation of -webkit-overflow-scrolling: touch
. The problem is seems pretty awful at first: if you use the new native scrolling behavior on elements that use or contain children with position:relative
, you may end up with some pretty gnarly rendering areas. This is particularly true for stuff that gets rendered off-screen.
Lucky for us, there is relatively (ahem) painless workaround. If you force Mobile Safari to use hardware acceleration when drawing the positioned elements, the rendering errors disappear. How do you do that? With a little 3D magic, via a no-move translation with this declaration: -webkit-transform: translate3d(0,0,0)
. Sprinkling this 3D fairy dust on your elements with position:relative
and your rendering issues will vanish.
One thing I have found is that sometimes, you need to apply the hardware acceleration hack to all of the child elements inside the scroller. This turned out to be true on a very complicated faux table in our really big HTML5 iPad app. The solution there was to use a scope universal selector. This isn’t the most performant selector, but it gets the job done in a way that working with the individual elements just didn’t cut it.
Update: I have found since writing this that even the hardware acceleration hack doesn’t cure all rendering ills. On the aforementioned complex layout, which for various reasons mimics a table, some of the elements in the cells were rendering incorrectly even with translate3d()
. The solution in this case was to fix the height of the elements. This is not ideal in all situations, of course, and I’ll be looking for alternatives in the future. Also, iOS 5.1 does not appear to fix the scrolling issues.
Filling the gaps
Another issue with the native scrolling in iOS 5 is backward and cross-browser compatibility. iOS 4 doesn’t support overflow scrolling or the native touch behavior; your elements won’t scroll at all with this technique before iOS 5. On other platforms, not every mobile browser support overflow:auto
or overflow:scroll
and support for -webkit-overflow-scrolling
is even more unlikely as you move beyond iPhones and iPads. So, what to do about it?
Fortunately, there’s a pretty nice new poyfill, Overthrow, that can help plug the scrolling holes. Like all polyfills, Overthrow seeks to use the latest and greatest built-in support when it can, and fills in the gaps with Javascript implementations when the current browser isn’t up to the task.
Using Overthrow is pretty simple. Include the script file in your page and define a CSS class like this:
Overthrow will try to figure out if the current browser is capable of using the native implementation; if it can, the class overthrow-enabled
will be added to the HTML
element and the native implementation will be used, with all it’s fast, bouncy goodness. If not, Overthrow will use it’s own scrolling implementation, which is as fancy. But, hey, your stuff will still scroll!
Overthrow is fairly new and it’s likely to get better, especially if you fork it and lend a hand.