The problem: On iOS the window-size changes when you scroll down. The URL bar minimizes and the toolbar at the bottom slides out of the screen. This creates 60px more space to display the page – nice, but when you have an overlay on your page which is set to a height of 100% (a very common approach) the result is an ugly gap at the bottom.

Using JavaScript to fix this might be an acceptable solution – especially in my case: An overlay used to indicate a loading process and disable any further interaction until loading is done.

Already the first lines of code, and I was done with that idea. It would require some sort of browser detection, something you should definitely avoid. And, since resize is not firing in this particular case – which event do you use? Onload, might be possible, in that case, you also need orientationchange, onscroll – surely not? All this for layout enhancement, there must be a better way.

In short, there is a better way: Using vh

With vw/vh, we can size elements to be relative to the size of the viewport. The vw/vh units are interesting in that 1 unit reflects 1/100th the width of the viewport. To make an element the full width of the viewport, for example, you’d set it to width:100vw.
Jonathan Snook – Sizing with CSS3′s vw and vh units

It is as simple as this:


.overlay {
	height: 100%; /* fallback for older browsers */
	width: 100%;
	height: 100vh; /* override */
	width: 100vh;
}

The gap is gone, no resize script written. When scanning the web for a solution, I found various answers/ comments which would use an absolute (or fixed) positioned element where all position dimensions (top, right, bottom, left) are set to zero. Although this works great to center the unkown, in my case it did not remove the gap produced by the dynamic viewport change.

One more word on this viewport behaviour on iOS. The additional visible screen height is useful and many developers (and designers) therefore adding this fabulous window.scrollTo(0, 1); code-line before doing anything else. If you do so, keep the following in mind:

  • If you implement it ‘just like this’, you kill hashtags, jumping to an anchor on page load will not work
  • You want to do this onload? Does not work – therefore you’ll need to add a timeout – means: there is a difference between the possible interaction of the user with the page (for example scrolling down) and script execution. Consequence – if the user scrolls you’ll send him back to page top – and that is really bad UX.
  • window.scrollTo works well in most situations on mobile safari, except when the iOS status bar is tapped. If it’s tapped, the page scrolls to the top and url bar is shown, and window.scrollTo doesn’t work anymore. To handle this, you’ll need to use an anchor / id element as a target (scrollintoview).

So, in case you really want to go for the 60 pixel plus – try something like this:


window.addEventListener("load", function() {
	setTimeout(function() {
		var scrollPos = window.pageYOffset || 
			document.documentElement.scrollTop || 
			document.body.scrollTop;
		if (scrollPos < 1) {
			window.scrollTo(0,1);
		}
	}, 0);
});

In my opinion, let the user interact as he wants and as he is used to, your page should work and look good – with and without the 60 pixel.