Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
December 20, 2011 12:00 am

Raising the Bar on Mobile

One of the primary challenges of designing for mobile devices is that screen real estate is often in limited supply. Through the advocacy of Luke W and others, weve drawn comfort from the idea that this constraint ends up benefiting users and designers alike, from obvious advantages like portability and reach, to influencing our content strategy decisions through focus and restraint. But that doesnt mean we shouldnt take advantage of every last pixel of that screen we can snag!

As anyone who has designed a website for use on a smartphone can attest, theres an awful lot of space on mobile screens dedicated to browser functions that would be better off toggled out of view. Unfortunately, the visibility of some of these elements is beyond our control, such as the buttons fixed to the bottom of the viewport in iOSs Safari and the WebOS browser. However, in many devices, the address bar at the top can be manually hidden, and its absence frees up enough pixel room for a large, impactful heading, a critical piece of navigation, or even just a little more white space to air things out.

So, as my humble contribution to this most festive of web publications, today Ill dig into the approach I used to hide the address bar in a browser-agnostic fashion for sites like BostonGlobe.com, and the jQuery Mobile framework.

Surveying the land

First, lets assess the chromes of some popular, current mobile browsers. For example purposes, the following screen-captures feature the homepage of the Boston Globe site, without any address-bar-hiding logic in place.

Note: these captures are just mockups " actual experience on these platforms may vary.

Screenshots of the Boston Globe website on an iPhone and a Windows 7 phone. On the left is iOS5s Safari (running on iPhone), and on the right is Windows Phone 7 (pre-Mango).

Screenshots of the Boston Globe website on Android and Blackberry. BlackBerry 7 (left), and Android 2.3 (right).

Screenshots of the Boston Globe website on Opera Mobile, Opera Mini and WebOS. WebOS (left), Opera Mini (middle), and Opera Mobile (right).

Some browsers, such the default browsers on WebOS and BlackBerry 5, hide the bar automatically without any developer intervention, but many of them dont. Of these, we can only manually hide the address bar on iOS Safari and Android (according to Opera Web Opener, Mike Taylor, some discussion is underway for support in Opera Mini and Mobile as well, which would be great!). This is unfortunate, but iOS and Android are incredibly popular, so lets direct our focus there.

Great API, or greatest API?

As it turns out, iOS and Android not only allow you to hide the address bar, they use the same JavaScript method to do so, too (this shouldnt be surprising, given that they are both WebKit browsers, but nothing expected happens in mobile). However, the method they use is not exactly intuitive. You might set out looking for a JavaScript API dedicated to this purpose, like, say, window.toolbar.hide(), but alas, to hide the address bar you need to use the window.scrollTo method!

window.scrollTo(0, 0);

The scrollTo method is not new, its just this particular use of it that is. For the uninitiated, scrollTo is designed to scroll a document to a particular set of coordinates, assuming the document is large enough to scroll to that spot. The method accepts two arguments: a left coordinate; and a top coordinate. Its both simple and supported well pretty much everywhere. In iOS and Android, these coordinates are calculated from the top of the browsers viewport, just below the address bar (interestingly, it seems that some platforms like BlackBerry 6 treat the top of the browser chrome as 0 instead, meaning the page content is closer to 20px from the top).

Anyway, by passing the coordinates 0, 0 to the scrollTo method, the browser will jump to the top of the page and pull the address bar out of view! Of course, if a quick call to scrollTo was all we need to do to hide the address bar in iOS and Android, this article would be pretty short, and nothing new. Unfortunately, the first issue we need to deal with is that this method alone will not usually do the trick: it must be called after the page has finished loading.

The browser gives us a load event for just that purpose, so well wrap our scrollTo method in it and continue on our merry way! Well use the standard, addEventListener method to bind the the load event, passing arguments for event name load, and a callback function to execute when the event is triggered.

window.addEventListener("load",function() {    window.scrollTo(0, 0);});

For the sake of preventing errors in those using browsers that dont support addEventListener, such as Internet Explorer 8 and under, lets make sure that method exists before we use it:

if( window.addEventListener ){    window.addEventListener("load",function() {        window.scrollTo(0, 0);    });}

Now were getting somewhere, but we must also call the method after the load events default behavior has been applied. For this, we can use the setTimeout method, delaying its execution to after the load event has run its course.

if( window.addEventListener ){    window.addEventListener("load",function() {        setTimeout(function(){            window.scrollTo(0, 0);        }, 0);    });}

Sweet sugar of Christmas! Hit this demo in iOS and watch that address bar drift up and away!

Not so fast…

Weve got a little problem: the approach above does work in iOS but, in some cases, it works a little too well. In the process of applying this behavior, weve broken one of the primary tenets of responsible web development: don’t break the browser’s default behaviour. This usability rule of thumb is often violated by developers with even the best of intentions, from breaking the browsers back button through unrecorded Ajax page refreshes, to fancy momentum touch scrolling scripts that can wreak havoc in all but the most sophisticated of devices. In this case, weve prevented the browsers native support of deep-linking to sections of a page (a hash identifier in the URL matching a page elements id attribute, for example, https://example.com#contact) from working properly, because our script always scrolls to the top.

To avoid this collision, well need to detect whether a deep link, or hash, is present in the URL before applying our logic. We can do this by ensuring that the location.hash property is falsey:

if( !window.location.hash && window.addEventListener ){    window.addEventListener( "load",function() {        setTimeout(function(){            window.scrollTo(0, 0);        }, 0);    });}

Still works great! And a quick test using a hash-based URL confirms that our script will not execute when a deep anchor is in play. Now iOS is looking sharp, and weve added our feature defensively to avoid conflicts.

Screenshot of the Boston Globe website on the iPhone, without an address bar.

Now, on to Android…

Wait. You didnt expect that we could write code for one browser and be finished, right? Of course you didnt. I mentioned earlier that Android uses the same method for getting rid of the scrollbar, but I left out the fact that the arguments it prefers vary slightly, but significantly, from iOS. Bah!

Differering from the earlier logic from iOS, to remove the address bar on Androids default browser, you need to pass a Y coordinate of 1 instead of 0. Aside from being just plain odd, this is particularly unfortunate because to any other browser on the planet, 1px is a very real, however small, distance from the top of the page!

window.scrollTo( 0, 1 );

Looks like were going to need a fork…

R UA Android?

At this point, some developers might decide to simply not support this feature in Android, and more determined devs might decide that a quick check of the User Agent string would be a reliable way to determine the browser and tweak the scroll value accordingly. Neither of those decisions would be tragic, but in the spirit of cross-browser and future-friendly development, Ill propose an alternative.

By this point, it should be clear that neither of the implementations above offer a particularly intuitive way to hide an address bar. As such, one might be skeptical that these approaches will stick around very long in their present state in either browser. Perhaps at some point, Android will decide to use 0 like iOS, making our lives a little easier, or maybe some new browser will decide to model their address bar hiding method after one of these implementations. In any case, detecting the User Agent only allows us to apply logic based on the known present, and in the world of mobile, lets face it, the present is already the past.

Writing a check

In this next step of todays technique, well apply some logic to quickly determine the behavior model of the browser were using, then capitalize on that model " without caring which browser it happens to come from " by applying the appropriate scroll distance.

To do this, well rely on a fortunate side effect of Androids implementation, which is when you programatically scroll the page to 1 using scrollTo, Android will report that its still at 0 because oddly enough, it is! Of course, any other browser in this situation will report a scroll distance of 1. Thus, by scrolling the page to 1, then asking the browser its scroll distance, we can use this artifact of their wacky implementation to our advantage and scroll to the location that makes sense for the browser in play.

Getting the scroll distance

To pull off our test, well need to ask the browser for its current scroll distance. The methods for getting scroll distance are not entirely standardized across popular browsers, so well need to use some cross-browser logic. The following scroll distance function is similar to what youd find in a library like jQuery. It checks the few common ways of getting scroll distance before eventually falling back to 0 for safety’s sake (that said, Im unaware of any browsers that wont return a numeric value from one of the first three properties).

// scrollTop getterfunction getScrollTop(){    return scrollTop  = window.pageYOffset ||document.compatMode === "CSS1Compat" && document.documentElement.scrollTop ||document.body.scrollTop || 0;}

In order to execute that code above, the body object (referenced here as document.body) will need to be defined already, or well risk an error. To determine that its defined, we can run a quick timer to execute code as soon as that object is defined and ready for use.

var bodycheck = setInterval(function(){    if( document.body ){        clearInterval( bodycheck );        //more logic can go here!!    }   }, 15 );

Above, weve defined a 15 millisecond interval called bodycheck that checks if document.body is defined and, if so, clears itself of running again. Within that if statement, we can extend our logic further to run other code, such as our check for the scroll distance, defined via the variable scrollTop below:

var scrollTop,    bodycheck = setInterval(function(){    if( document.body ){        clearInterval( bodycheck );        scrollTop = getScrollTop();    }   }, 15 );

With this working, we can immediately scroll to 1, then check the scroll distance when the body is defined. If the distance reports 1, were likely in a non-Android browser, so well scroll back to 0 and clean up our mess.

window.scrollTo( 0, 1 );var scrollTop,    bodycheck = setInterval(function(){    if( document.body ){        clearInterval( bodycheck );        scrollTop = getScrollTop();        window.scrollTo( 0, scrollTop === 1 ? 0 : 1 );    }   }, 15 );

Cashing in

All of the pieces are written now, so all we need to do is combine them with our previous logic for scrolling when the window is loaded, and well have a cross-browser solution of which John Resig would be proud. Heres our combined code snippet, with some formatting updates rolled in as well:

  1. (function( win ){
  2. var doc = win.document;
  3. // If there's a hash, or addEventListener is undefined, stop here
  4. if( !location.hash && win.addEventListener ){
  5. //scroll to 1
  6. window.scrollTo( 0, 1 );
  7. var scrollTop = 1,
  8. getScrollTop = function(){
  9. return win.pageYOffset || doc.compatMode === "CSS1Compat" && doc.documentElement.scrollTop || doc.body.scrollTop || 0;
  10. },
  11. //reset to 0 on bodyready, if needed
  12. bodycheck = setInterval(function(){
  13. if( doc.body ){
  14. clearInterval( bodycheck );
  15. scrollTop = getScrollTop();
  16. win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
  17. }
  18. }, 15 );
  19. win.addEventListener( "load", function(){
  20. setTimeout(function(){
  21. //reset to hide addr bar at onload
  22. win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
  23. }, 0);
  24. } );
  25. }
  26. })( this );

View code example

And with that, weve got a bunch more room to play with on both iOS and Android.

Screenshot of the Boston Globe website on an Android phone, without an address bar.

Break out the eggnog

…because were not done yet! In the spirit of making our script act more defensively, theres still another use case to consider. It was essential that we used the windows load event to trigger our scripting, but on pages with a lot of content, its use can come at a cost. Often, a user will begin interacting with a page, scrolling down as they read, before the load event has fired. In those situations, our script will jump the user back to the top of the page, resulting in a jarring experience.

To prevent this problem from occurring, well need to ensure that the page has not been scrolled beyond a certain amount. We can add a simple check using our getScrollTop function again, this time ensuring that its value is not greater than 20 pixels or so, accounting for a small tolerance.

if( getScrollTop() < 20 ){    //reset to hide addr bar at onload    window.scrollTo( 0, scrollTop === 1 ? 0 : 1 );}

And with that, were pretty well protected! Heres a final demo.

The completed script can be found on Github (full source: https://gist.github.com/1183357 ). Its MIT licensed. Feel free to use it anywhere or any way youd like!

Your thoughts?

I hope this article provides you with a browser-agnostic approach to hiding the address bar that you can use in your own projects today. Perhaps alternatively, the complications involved in this approach convinced you that doing this well is more trouble than its worth and, depending on the use case, that could be a fair decision. But at the very least, I hope this demonstrates that theres a lot of work involved in pulling off this small task in only two major platforms, and that theres a real need for standardization in this area.

Feel free to leave a comment or criticism and Ill do my best to answer in a timely fashion.

Thanks, everyone!

Some parting notes

I scream, you scream…

At the time of writing, I was not able to test this method on the latest Android 4.0 (Ice Cream Sandwich) build. According to Sencha Touchs browser scorecard, the browser in 4.0 may have a different way of managing the address bar, so Ill post in the comments once I get a chance to dig into it further.

Short pages get no love

Todays technique only works when the page is as tall, or taller than, the devices available screen height, so that the address bar may be scrolled out of view. On a short page, you might work around this issue by applying a minimum height to the body element ( body { min-height: 460px; } ), but given the variety of screen sizes out there, not to mention changes in orientation, its tough to find a value that makes much sense (unless you manipulate it with JavaScript).


Original Link:

Share this article:    Share on Facebook
No Article Link

24 Ways

# 24 ways is an edgeofmyseat.com production. # Edited by Drew McLellan and Brian Suda. # Assisted by Anna Debenham and Owen Gregory.

More About this Source Visit 24 Ways