Fixed Elements and Scrolling Divs in iOS 5

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/

  • Vladimir Bozic

    Hi, great article. I have one question what happens when fixed div is less then 100% in width. When you use zoom, fixed div stay the same width. Also when you have custom scrollable divs like news sliders, IScroll brokes them.

  • http://www.kylejlarson.com Kyle Larson

    If you allow the user to zoom, the fixed div will increase along with the rest of the page because zoom increases the size of all content. You could probably have a javascript resize the element after zooming to be a % of the device width, but it would likely zoom first and then resize.

    I haven’t tested iScroll too much to comment on it breaking divs or adjusting other elements. I switched solutions when I realized it didn’t work well with form fields.

    Also, keep in mind that allowing zoom is helpful in some cases but can quickly cause usability issues with scrolling divs because of having both a scrolling browser window and a scrolling div within it.

    Thanks for the reply!

  • http://twitter.com/2ne James Toone

    In my design I have a tab at the bottom of the screen which is using position fixed. When clicked on it opens a ul with the -webkit-overflow-scrolling: touch; property on it. But when I reach the top or bottom of this list the DOM then starts to scroll the page behind. Is there a way to stop this?

  • http://www.kylejlarson.com Kyle Larson

    Hi James,

    Sounds like the default behavior of the browser. Typically it’ll bounce once when scrolling, but then like you mentioned it’ll scroll the full page. The fix would be adding one of the options above (Scrollability, iScroll, ScrollFix) and putting it on your scrolling element. It’s a bit tricky to get setup but I think it should work. Also if you scroll on any other elements it’ll still cause the page bounce unless you’ve got locked down with the PreventDefault mentioned above. Hopefully that helps a bit.