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: websitepowerups.com/files/iosdemo/

Tags: , , , , ,

« Older     Newer »

Leave a Reply

24 Comments

  1. 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.

    • 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!

  2. 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?

    • 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.

  3. Rhodyman

    I tested your demo with links.

    Everything seems to work on my New iPad but on my iPod Touch (1st gen) the scrolling doesn’t work. As you might guess, my web design experience is with conventional HTML using frames and I have no iOS web design experience at all. But I am encouraged by the ability to program for my New iPad.

    Here is the demo with links: http://macatlehighvalley.org/iosdemo2.html

    • Kyle Larson

      Interesting. I think that is because iOS 5 only works with the newer iPod Touch models. It’s good to hear you were able to get it working fine with your iPad though and thanks for the comment.

  4. Social Blogsite

    I don’t know what I’m supposed to see, but the demo does nothing out of ordinary in portrait mode, and when the device is rotated into landscape mode, the footer stays at the bottom of the page, hiding all the content, which doesn’t scroll, so the hidden part gets permanently lost.
    The whole page bounces when trying to scroll up and down and there are no margins, so the text is stuck at the edge of the screen.

    • Kyle Larson

      What type of device were you viewing it on? It should work on iPhone/iPod that has at least iOS version 5. I also added a few more paragraphs so now it should work on iPad as well (didn’t initialize the scrolling previously since the text didn’t overflow the page). The content should scroll with the header/footer being fixed.

      • Gary Fonseca

        I just tried this on my iphone 4s ios 5.1 and it actually behaves as this guy described, once you turn your device, it makes the whole browser bounce and the footer stays below the navigation bar on the bottom and the size of the page becomes bigger so it’s almost imposible to read the contents because the footer and header gets really close to each other… I’m experience this kind of problems right now in a solution I’m implementing so if you come up with a fix for that I will appreciate it, if I do, I’ll let you know!

  5. Khoa

    This is exactly what I was looking for. Thanks a lot. Have you found a solution to make this work on iOS4?

  6. mydigitalself

    I have a different problem… I’ve got a div fixed to the bottom of the page. When I scroll down the page, the div jumps up with the initial scroll and them settles back down on the bottom. Once it’s done this once, it then continues to stick properly to the bottom.

    Using CSS, roughly as:
    .fixed {
    position: fixed;
    bottom: 0px;
    width: 100%;
    padding: 20px;
    background-color: green;
    }

    Any idea/workaround for this?

      • mydigitalself

        Hi Luke, yeah I took a similar strategy to Kyle’s example where rather than having the content in the body, the content is in a content-div:

        .contentBody {  position: absolute;  top: 0;  left: 0;  right: 0;  bottom: 60px;  width: 320px;  max-width: 320px;  overflow: scroll;  -webkit-overflow-scrolling: touch;}

        and then obviously the footer element that you want to stick to the bottom is bottom: 0px and has a height of 60px. I also then used the scrollfix library to smooth things out: http://www.kylejlarson.com/files/iosdemo/scrollfix.js

        It was a bit fiddle, but eventually managed to get it working, check out Kyle’s link below which is basically what I modified to suit my purposes.

  7. Mark L.

    Been viewing a lot of developer sites and your site is standout in that it is extremely clean and technically sound. Also the articles are useful. (This is not russian spam, just a friendly compliment.) Good job.

  8. Susanna Larsen

    Hello, This is working great for me, the only problem I have is that I used the code to prevent the bounce when I have little content in. The only problem is I have a fixed sidebar with a scrollable div inside for scrolling down (as it’s a long menu) – when I use the code to stop the bounce it stops me being able to scroll the left side menu down. sorry if you have already covered this but I tried a few different ways described on here and couldn’t get it to work. Any ideas? Thanks

Back to top