Skip to main content

Fix Your Position: Even In iOS 4

10-19-11 Rob Tarr

Rob Tarr shares about detecting postion:fixed in the browser—trying to find a solution that will work with iOS 4.

Recently, I’ve been given a few designs requiring a nav fixed to the top of the browser. In a perfect world, I could set position:fixed on the nav, sit back, and bask in the glory of CSS simplicity. Unfortunately, in the real world it isn’t that easy. Surprisingly, the browser I’ve had the most difficulty with is Mobile Safari prior to version 5.

I’ve found multiple solutions to this problem; however, none of them really seem to work. These solutions are typically testing the element for position:static behavior, which is common in older browsers (I’m looking at you IE). Older versions of Mobile Safari, however, incorrectly report no support for position:fixed in Mobile Safari 5.

I was really hoping to build an extension to Modernizr (more on that later), so I went to the Modernizr docs and found a promising example:

Modernizr.addTest('positionfixed', function () {
  var test  = document.createElement('div'),
      control = test.cloneNode(false),
      fake = false,
      root = document.body || (function () {
        fake = true;
        return document.documentElement.appendChild(document.createElement('body'));
      }());

  var oldCssText = root.style.cssText;
  root.style.cssText = 'padding:0;margin:0';
  test.style.cssText = 'position:fixed;top:42px';
  root.appendChild(test);
  root.appendChild(control);
  
  var ret = test.offsetTop !== control.offsetTop;
  
  root.removeChild(test);
  root.removeChild(control);
  root.style.cssText = oldCssText;
  
  if (fake) {
     document.documentElement.removeChild(root);
  }
  
  return ret;
});

This works great on the desktop since most current browsers support position:fixed, but it fails on the iPhone making it useless for my purposes. After playing with a few variations of this, I decided to set it aside for another day.

Now, here is my solution. I make no claims that it is elegant, clever, or genius. But it works.

To add it to your page, simply looks like this:

<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script src="js/jquery.ba-throttle-debounce.min.js"></script>
<script src="js/jquery.fix.js"></script>
<script src="hasPositionFixed/js/jquery.hasFixed.js"></script>
<script>
  $.hasPositionFixed(function() {
    $('nav').fix();
  });
</script>

The initial call to $.hasPositionFixed creates a hidden iframe on the page that loads in iframe-content.html. The JavaScript in iframe-content.html creates a fixed position element, scrolls the iframe, and checks the position of that element to see if it’s correct for a fixed position element. It then sets ‘hasPositionFixed’ in the parent window based on the result of the scroll test.

Because it takes some time, load the iframe and do the testing. The test becomes asynchronous at this point, as $.hasPositionFixed accepts a callback to be run once support has been determined. With this method, things such as initializing a sticky nav bar can be done in an intelligent manner.

Like I said, this is a somewhat brute force attempt to detect support. I looked for an elegant solution, but so far it has evaded me. If you have thoughts or suggestions, fork the repo to see what you can do, drop me a line on twitter. I would love to see what solutions the larger JavaScript community comes up with.

Head over to Github to check out the files for yourself.

Related Content

User-Centered Thinking: 7 Things to Consider and a Free Guide

Want the benefits of UX but not sure where to start? Grab our guide to evaluate your needs, earn buy-in, and get hiring tips.

More Details

See Everything In

Want to talk about how we can work together?

Katie can help

A portrait of Vice President of Business Development, Katie Jennings.

Katie Jennings

Vice President of Business Development