Thursday, June 12, 2014

Easy CSS fix for fixed positioning on Android 2.2 and 2.3

When it came to cross-testing a design on an old Android recently I came across a familiar problem to most front-end developers. The CSS property/value pair of position: fixed; doesn’t work.
Update: 2/3/14 – Since writing this post I actually came across anddocumented a related issue regarding the poor performance of fixed position elements. Turns out that on LoDPI screens, WebKit and Blink currently promote elements that have qualifying property/value pairs (such asbackface-visibility: hidden;) to their own layer – hence the improved performance of fixed position elements on older Android devices (typically with lower resolution screens).
By default, on Android < 2.3 devices, elements that are set toposition: fixed; in CSS behave as if they are ‘static’ or part of the normal document flow.
Android 2.3 actually supports the position: fixed; property/value pair, but as noted by Brad Frost it was necessary to disable page scaling to get it working. For example, sticking this in the head:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no"/>

Enter -webkit-backface-visibility: hidden;

In the last few months, I’ve had plenty of odd fun with stacking contexts on mobile devices so I thought I’d try something.
It’s resulted in a real simple fix for this problem in most Android 2.2–2.3 browsers. Just add:
-webkit-backface-visibility: hidden;
To the position: fixed; element. With that in place, fixed position elements seem to behave exactly as they should on most Android 2.2 and 2.3 devices.

A live example

If you want to test this out on your own device, try viewing this Codepen:http://codepen.io/benfrain/full/wckpb
I have tested this on a physical HTC Desire running 2.3.3 and a Nexus One Android 2.2 device in the Android Emulator (I’m a little concerned that the emulator may not be entirely accurate so welcome more concrete info) and everything works as I would have expected on a ‘normal’ device.
As an added bonus, on the slim amount of devices/emulators I’ve checked on, it’s not necessary to disable zooming to get this working.
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes"/>
Is this due to -webkit-backface-visibility: hidden; promoting things to a new stacking context and therefore forcing a different render? I really have no idea (although I’m assuming so). However, it seems pretty robust and scrolling seems no worse than usual.

No joy on Android 2.1 or Windows Phone 7.5

Sadly, this little ‘hack’ has no effect on Android 2.1 and only applies to WebKit based devices. For example, to confirm what you would expect, it won’t work on Windows Phone 7.5 (as it’s not -webkit- based).
I tested on a HTC Mozart device running OS 7.10.7720.68 to confirm and an emulated Android 2.1 device. On those handsets, fixed position elements remain statically positioned. Definitely fork your code for fixed position elements on the Windows Mobile < 8 platform (e.g. with Modernizr).
I usually use an arbitrary non-supported feature to target a support fork for other devices (such as SMIL). I’ve seen a Gist for a specific position: fixed extension but I haven’t tried it myself. That may well be a better option.

Conclusion

If you need a fixed position element on Android 2.2 or 2.3, adding-webkit-backface-visibility: hidden; to the element seems to make it ‘work’.
However, if you try that Codepen on your Android 2.2 or 2.3 device and it doesn’t work, please let me know and I can amend this post.

Updates

Jelmer Borst has tested a few more devices via DevAnywhere:
LG Thrill and Motorola Droid 2 work well but Samsung Galaxy SII running Android 2.3.6 seems to move and then snap back in place so this doesn’t seem to be the panacea for the problem I had hoped for on all Android devices.

No comments:

Post a Comment