Fixed Elements and Scrolling Divs in iOS 5
November 11th, 2011 by Kyle Larson
I’ve been playing around with creating a web app for iOS and testing some of the new features in mobile safari that were released with iOS 5. Fixed divs and the ability to scroll a div are common in iOS native apps but were difficult to implement in a web app. These have both been added in the new safari but they still require some work-arounds to make them function like you probably want.
Fixed Elements
The first versions of iOS didn’t support fixed elements. They would fix correctly to the screen, but then they would scroll along with the rest of your content. In iOS 5, fixed elements (e.g. position: fixed; bottom: 0;) work as expected.
Keep in mind you’ll want to use the iOS meta tags if you’re designing specifically for the device. The first line below prevents the default scaling for sites that aren’t built for to adapt to the screen size and the second allows you to save the site and run it without the browser bars.
<meta name="viewport" content="initial-scale = 1.0, user-scalable = no">
<meta name="apple-mobile-web-app-capable" content="yes" />
The first issue with the fixed elements is that your site will still have the ‘bounce’ effect when scrolling past the top/bottom of your content that reveals the browser chrome (background beneath your page as it bounces). If your site doesn’t need to scroll this is a very simple fix, just add a line to prevent the default action when moving your finger:
<script type="text/javascript">
document.addEventListener('touchmove', function(event) {
event.preventDefault();
}, false);
</script>
If you want scrolling areas, but don’t want the browser chrome to show then continue on!
Scrolling Divs
Another new feature in iOS 5+ is divs can finally scroll. To make a div scroll you’ll just need to add the following (this example has a fixed header but adjust the positioning to fit your app):
.scrollable {
position: absolute;
top: 50px;
left: 0;
right: 0;
bottom: 0;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
The trick here is adding the -webkit-overflow-scrolling to anywhere you’d normally set your overflow. The downside of Apple’s implementation is that the div doesn’t bounce as you’d hope when you are already at the absolute top or bottom. Instead of bouncing the div being scrolled, it bounces the whole page and reveals the browser chrome. This is where you’ll need a javascript fix.
Fixing the Scrolling Element
Thanks to the development community there are several great options out there that you can implement. These include Scrollability and iScroll. My issue with iScroll was that it doesn’t play well with form elements if you have them in your app. At the time of this article, Scrollability’s author says it isn’t ready for release usage, but it seems to be a great solution if you want to test it. If you want your app to work well with old implementations of iOS you’ll need something similar to this (plus you’ll also need to code around the fixed elements issue), but for now we’re only addressing iOS 5+ and we’ll will save fallbacks for a different article.
The solution I like the best for iOS5 is also the simplest. Joe Lambert’s ScrollFix increases the page slightly when at the top or bottom to make your div bounce instead of the whole browser. It’s very simple to add to an element that you already have scrolling:
<script type="text/javascript" src="scrollfix.js"></script>
<script type="text/javascript">
var scrollingContent = document.getElementById("myScrollingDiv");
new ScrollFix(scrollingContent);
</script>
If you have multiple scrolling divs just apply this again to those divs. For my app I’ve got several divs which either shown or hidden and each has this script applied. We’re almost there, but two possible issues still exist. When scrolling on our fixed elements and our scrolling elements that don’t have enough content to overflow the page, the browser chrome is still revealed.
Preventing Fixed Headers from Showing Browser Chrome
This is a pretty simple fix, similar to what was described above. In my fixed header I’ve got multiple elements, so I made sure that each element or it’s parent had the class “noBounce” and then applied the following script:
document.addEventListener('touchmove', function(event) {
if(event.target.parentNode.className.indexOf('noBounce') != -1
|| event.target.className.indexOf('noBounce') != -1 ) {
event.preventDefault(); }
}, false);
This prevents the default bounce when dragging those classed elements, but doesn’t break the div that you want to scroll.
Adapting for Elements That Don’t Overflow
When creating my code some of the divs for my main content overflowed the browser window which triggers the scrollbar and bounce fix, but others didn’t. There are several ways to address this, but the easiest solution I came up with just extends all divs so they have at least a bit of scroll. To do that I added this jQuery script (you could do something similar in straight JS as well) to all my content divs:
<div class="scrollable">
<div class="contentWrapper">
Your content
</div>
</div>
<script type="text/javascript">
$('.contentWrapper').css('min-height', $(window).height() - 55 + 'px');
</script>
You’ll need to adjust the “- 55″ portion based on your application. I have it in there to prevent it from having too much scroll because of my fixed header.
Here’s a really basic demo if you want to test it out in your mobile browser: kylejlarson.com/files/iosdemo/

Pingback: davidcool :: portfolio :: blog » Mobile Code Updates!