iOS 5: Fixed Positioning and Content Scrolling

iOS 5: Fixed Positioning and Content Scrolling

Tutorial Details
  • Technology: iOS 5 + Mobile Safari
  • Difficulty: Beginner
  • Completion Time: 15 - 30 Minutes

Two of the most celebrated enhancements to Safari on iOS 5 are fixed positioning and content scrolling support. This tutorial will teach you how to take advantage of this change and what the implications are for stop-gap JavaScript libraries like iScroll.


In a previous tutorial, I talked about iScroll and how this great little plugin helped fix an issue with iOS Webkit (5.0 and below) and Android Webkit (2.1 or below), in which there was no native support for fixed positioning or scrollable content areas.

So, after a weekend of running various tests, it’s nice to confirm that the iOS 5 Safari update now tackles both of these issues and we now have full native support for them. It has been in the pipeline for some time in terms of the beta releases for iOS 5, but you can never guarantee that these things will make it to the final release.

In this tutorial, I will discuss this change at length and also teach you how to convert the iScroll project from our previous tutorial to using the new CSS properties.


What Does This Change Mean?

To be explicit, we now have the ability to build web apps that have fixed headers and footers using position:fixed as well as scrollable content in between using -webkit-overflow-scrolling. This allows us to build applications with a more native feel without needing to resort to a third party plugin, such as iScroll. As you will see though, for now there are still some good reasons to depend on third party libraries like iScroll.


The Caveats

While this change is great news for web developers, there are a few caveats worth discussing.

First, the most obvious one is that this feature is currently only supported in Safari 5.1. While the new 4S has seen record pre-orders, and many past users have already upgraded to iOS 5, there are still going to probably be a substantial amount of iOS device users on 5.0 or lower.

Next, there is currently no way to remove the scrollbar that appears in the side of the content area. You could try doing something with the webkit-scrollbar CSS method to change colors etc, but I don’t see this as a massive issue. The scrollbar is a nice UI element that makes the user aware of where they are in the document. Why bother removing it?

Another issue: there’s no way to define the ‘rubber banding’ area of the browser, as it will just rubber band at the very top and bottom of the screen area, including the address bar. I had begun working on a bit of JavaScript to manually offset the scrollTop value at either end by 1, but then I found Joe Lambert had already done this with scrollFix.js.

Finally, the scrolling momentum currently has no speed control. This would be more of a nice-to-have.

That’s enough with the issues, let’s take a look at how we can begin using the newly supported CSS!


Step 1. Remove the JavaScript

Let’s take a look at how to convert our past project to using the new CSS rules. We’ll use our previously built page with iScroll for demonstration.

The first thing to do is to remove the JavaScript include and the iScroll call from the bottom of the document, so you end up with a plain HTML and CSS file like the one below:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes" />
<title>Web App Template</title>
<style type="text/css" media="all">
body,ul,li {
	padding:0;
	margin:0;
	border:0;
}
body {
	font-family:helvetica;
}
header{
	background-color:#deb500;
	position:absolute;
	z-index:2;
	top:0; left:0;
	width:100%;
	height:45px;
	padding:0;
}
footer {
	background-color:#c27b00;
	position:absolute;
	z-index:2;
	bottom:0; left:0;
	width:100%;
	height:48px;
	padding:0;
	border-top:1px solid #444;
}
header, footer{
	font-size:20px;
	text-align:center;
	color:#f3f3f3;
	font-weight:bold;
	text-shadow:0 -1px 0 rgba(0,0,0,0.5);
	line-height:45px;
}
#wrapper {
	position:absolute;
	z-index:1;
	top:45px; bottom:48px; left:0;
	width:100%;
	background:#aaa;
	overflow:auto;
}
#scroll-content {
	position:absolute;
	z-index:1;
	width:100%;
	padding:0;
}
ul {
	list-style:none;
	padding:0;
	margin:0;
	width:100%;
	text-align:left;
}
li {
	padding:0 10px;
	height:40px;
	line-height:40px;
	border-bottom:1px solid #ccc;
	border-top:1px solid #fff;
	background-color:#fafafa;
	font-size:14px;
}
</style>
</head>
<body>
<header>
	Web App Template
</header>
<div id="wrapper">
	<div id="scroll-content">
		<ul>
		<li>Some Stuff</li>
		<li>More Stuff</li>
		<li>Big Stuff</li>
		<li>Small Stuff</li>
		<li>Geek Stuff</li>
		<li>Nerd Stuff</li>
		<li>Fast Stuff</li>
		<li>Slow Stuff</li>
		<li>Good Stuff</li>
		<li>Bad Stuff</li>
		<li>Your Stuff</li>
		<li>My Stuff</li>
		<li>Their Stuff</li>
		<li>Our Stuff</li>
		<li>Super Stuff</li>
		<li>Uber Stuff</li>
		<li>Stuff Stuff</li>
		<li>French Stuff</li>
		<li>German Stuff</li>
		<li>English Stuff</li>
		<li>American Stuff</li>
		<li>Stuff</li>
	</ul>
	</div>
</div>
<footer>
	Some Footer Content
</footer>
</body>
</html>

Step 2. Adjust the CSS

We don’t need to change our HTML at all. We just need to adjust some of our CSS and add one new class.

-webkit-overflow-scrolling : auto;

This is the new class that was introduced around beta 2 of iOS 5, and it is the one that gives us the nice momentum scrolling. By default, it is set to auto, which gives the scrolling a rigid look and feel. To give your scrolling area a more native feel, set this property to touch.

-webkit-overflow-scrolling : touch;

Now, apply this style to the scroll-content div and we are going to remove some of the styles from the wrapper, mainly the positioning and overflow. In fact, we really don’t need the wrapper div at all, so you can remove it if you wish, but I like to have a div around just to wrap anything if need be. The two CSS rules should look like below:

#wrapper {
	z-index:1;
	width:100%;
	background:#aaa;
}
#scroll-content {
	position:absolute;
	top:0;
	z-index:1;
	width:100%;
	padding:0;
	-webkit-overflow-scrolling:touch;
	overflow:auto;
}

Step 3. Fixing the Header and the Footer

Before, we had the header and footer set to absolute (as fixed was not supported). We can now go ahead and position these in the CSS using the fixed rule to stop them from scrolling away on the screen.

header{
	background-color:#deb500;
	position:fixed;
	z-index:2;
	top:0; left:0;
	width:100%;
	height:45px;
	padding:0;
}
footer {
	background-color:#c27b00;
	position:fixed;
	z-index:2;
	bottom:0; left:0;
	width:100%;
	height:48px;
	padding:0;
	border-top:1px solid #444;
}

If you don’t know the difference between fixed positioning and absolute positioning, the short version is that absolute positioning is a defined position relative to its parent element. Fixed positioning is a position fixed within the viewport.

You will now have a scrolling area with a fixed header and footer, without the use of Javascript!


The Future

It’ll be nice when other browsers play catch up (Window phone I am looking at you!) and the majority of users are on an Android OS higher than 2.1, but the near future should see some nice improvements on the webkit based browsers. With speed increasing on each iteration, apps made with web-based technologies might quickly overtake native applications. All we need now are more native APIs!


Where Does This Leave iScroll?

iScroll still has a place at the moment. There are many parameters we can pass through to the scrolling method to give some additional options to the way our scrolling works and looks that can’t currently be achieved without writing custom JavaScript…so why not use what is already available?

There is also the issue of browser support. Maybe you really need the fixed toolbars for you project in older implementations of the webkit browser. Well, no problem. If it’s the best thing for the project, then there is no shame in using one of the existing JavaScript libraries to achieve it. However, I would suggest using the native implementation whenever possible.

Going beyond the simple scrolling functionality, the iScroll library also offers some great supplemental functionality such as “Pull To Refresh”, “Pinch / Zoom”, and “SNAP / Snap to Element”. We can cover these in a later iScroll tutorial, as they are still useful and relevant features.

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.miketheindian.com Mike

    I just built a web app for the company I work for that uses iScroll. We are planning on getting rid of it and using the new fixed positioning update in iOS5. The iScroll method has been a pain. Certain input elements don’t work, and you have to put in a “hack” to get around it.

    Since it is an internal application, we can tell everyone to update to iOS5 or you will just have to scroll down to the bottom to get to the bottom tool bar.

    One potential problem with this new update, is when I build websites that have fixed positioning, I know that up until now, the iPhone changes it to absolute positioning basically. That is fine, but when I went back to a site that I built with ios5, the fixed element is now stuck in the middle of the screen which wasn’t expected.

    • Shaun
      Author

      iScroll can be a pain in certain circumstances, but I have also found it to be a blessing in others. It’s great that the Safari team have finally decided to support fixed positioning and even better when you are able to use it, such as with your internal application.

      Backward compatibility might be an issue for those looking to go ahead and remove iScroll from older applications. iScroll did require that certain elements to be positioned absolutely in order for the plugin to work, so my advice would be just to double check your containers / scroll area positioning. It sounds like something might be positioned absolute & not taking up any space in the document.

  • http://db.tt/rJObqWZZ Don

    Is this a bug in the implementation of position:fixed?
    http://db.tt/rJObqWZZ

    • Shaun
      Author

      There certainly seems to be some issue with this – which could be a big usability issue if not fixed. I will continue to look at it and see if it’s something that can be sorted, but nice find.

      Have you reported it to the Mobile Safari team?

  • http://argyleink.com Argyle

    i’m livin in this same world right now, web apps, but i’m using jQuery Mobile. they’ve updated their build to include this support. finally!

  • http://twitter.com/jitendravyas Jitendra Vyas

    iScroll have problem with form elements so I don’t use it

  • http://twitter.com/jitendravyas Jitendra Vyas

    I created this demo using the code in tutorial. But why horizontall scrollbar is coming http://jsfiddle.net/jitendravyas/3Bqbf/show/

    • https://plus.google.com/103805723729979551716 Shaun
      Author

      Hi Jitendra,

      I had a look at the page in an iPhone 4 running iOS5 and it seems that you probably need to make sure that you are using the meta viewport tag –

      the width=device-width is the one that doesn’t seem to be set.

      Thanks

  • Will Morris

    I’ve found that if you have a text input in a position:fixed , when you focus on the input box, the iOS keyboard comes up and the entire header is moved to the center of the screen.

    This isn’t desirable behavior. If I have a header fixed to the top of the screen, I want it to stay there. Have you experienced this and do you know of a work-around?

    Thanks!

    • Verb

      @Will There is a jquery workaround that I implemented on one of my sites. It pretty much hides the fixed element when a user focuses on the input field and shows once unfocused.

      Example code:

      $(document).ready(function() {

      $(‘input’).focus(function() {
      $(‘.header_fixed’).css(‘display’, ‘none’);
      }).focusout(function() {
      $(‘.header_fixed’).css(‘display’, ‘block’);
      });

      });

      Hope this helps!

  • http://www.adworkshop.com Andrew Carolla

    We have a website with a fixed background image on the body. The content simply scrolls over this background image in all browsers, but the background-position:fixed property does not seem to work on the iPad. Any insight into this?

  • http://justinavery.me Justin Avery

    I have a horizontal unordered list product navigation element that is 2300px wide. I have a wrapper div which only displays 600px of the navigation allowing the user to scroll through the different products.

    I updated the -webkit-overflow-scrolling : touch; because the standard scroll method was too static and rigid and I wanted the momentum on the scroll.

    Now there is an issue through, and the images disappear while the scroll is in momentum, and paint on the screen once the scroll momentum stops.

    Is this a know bug or an issue with my implementation?

    code can be seen http://dev.justinavery.me/colemanipad/index2.html

    • Ayko

      I’ve noticed that -webkit-overflow-scrolling : touch; has got some troubles with relative positioning.
      Maybe if you remove the position: relative on #productNav the scrolling will work without the disappearing images.

  • zhaiyun

    #scroll-content {
    position:absolute;
    top:0;
    z-index:1;
    width:100%;
    padding:0;
    -webkit-overflow-scrolling:touch;
    overflow:auto;
    }

    the scroll content is between fixed header and footer. why assigh top to 0? why not need to assigh bottom of scroll-content.

  • Alexandre

    Conflict with z-index ?

    I have found that setting
    -webkit-overflow-scrolling:touch;
    can silently overrides the z-index property, so beware. I have not yet fully investigated but it appears that the DIV with scrollable content will also appear on top of the z-index layers, so any other DIV with a very high z-index positioned to be displayed over the scrollable DIV will now be partially hidden.

  • jason

    Is there a way to prevent the page showing the grey over extended area when scrolling? When you scroll using this technique and you get to the end, you can continue to push up the page and it reveals the grey background on the ios device. Is there a way to prevent that? When googling about this, I found that some suggest the following code, but all this will do with this technique is prevent any scrolling at all.

    document.addEventListener(‘touchstart’, function (e) { e.preventDefault(); }, false);

    This was found at: http://stackoverflow.com/questions/2890361/disable-scrolling-in-an-iphone-web-application

  • http://boxfish.com Daniel

    Hi

    I have been playing around with -webkit-overflow-scrolling:touch; for a while and I getting randomly the following problem:

    I only need scroll top/bottom ( width is fixed ), but sometimes the user needs to scroll left and right to
    trigger the vertical scroll. In other words, the user has to scroll horizontally to make the container scroll vertically.

    again it is random, sometime it just works.

    I don’t have a live example at the moment, but I can post some code if needed.

    any thoughts ?

    Cheers

  • James Henderson

    Have you noticed that if you navigate away from the page with your new code on, and then go back using the iOS back button, that the fixed header and footer have disappeared but then they re-appear when you move the content

  • http://wpapptouch.com/ Gino

    Hi, i tried a with the scrollFix.js version and added a footer to it, it work well but i want to find a way to not let scroll down the page when you scroll down the header or the footer

    http://jsfiddle.net/onigetoc/MVuzY/

    Full screen
    http://jsfiddle.net/onigetoc/MVuzY/show/

  • Vivekanand Kandagatla

    Hi, I need a quick help on the example which is shown in the article – on page load / after page render in need to scroll the content to particular position – like for example, here in this example I would like to scroll to “Their Stuff” on page load / after page render. When I am doing like window.scrollTo(0,constactvalue) [ex: window.scrollTo(0,90)] the page is scrolling but the buttons on the fixed header were not working – it is working only when user explicitly scrolls the page up and down.

    Could you please let me know how can be this resolved.

  • trndxx

    setTimeout(function(){ $(object).css(‘position’,'fixed’) },0);
    setTimeout(function(){ $(object).css(‘position’,'absolute’); $(window).scrollTop(0); },10);