16
votes

In a web application that I am working on, I tried out iOS 7.1's new minimal-ui feature (see safari fullscreen in iOS 7.1 with minimal-ui meta tag), but I am seeing an issue where an 84px-high gray area appears at the bottom when I switch from portrait to landscape. Also, document.body.scrollTop changes to 64 after switching to landscape.

You can see the issue using this "Hello World" web app: http://www.creativepulse.gr/media/blog/2014/20140123-hello-world/example.html

When I load the app in Mobile Safari on iOS 7.1 iPhone Retina Simulator, everything is fine in portrait mode. However, switching to landscape, the gray area immediately appears.

What is a good way to fix this issue?

7
Can you please attach screenshots? I think I may be having the same issue but I want to confirm.Stoutie
Is this what it looks like? Screenshot from my jsbin.Stoutie
@stoutie: Yes, that's it.Daniel Trebbien

7 Answers

16
votes

Scrolling to the top after the page has rendered helped me. It somehow causes a rerendering and the grey box disappears, however I can't explain what exactly Safari does internally:

window.scrollTo(0, 0);
12
votes

I tried for a while to fix this with no luck. I finally decided to do a test where I:

  1. Created a new HTML document with the minimal-ui meta tag.
  2. Left the body of the document empty (no HTML tags), and no styles.

Tested this and the issue still occurs.

The only conclusion I could come up with is that this is a bug in IOS 7.1, so I submitted a bug report to Apple. It was reported as BUG #: 16335579.

Note that Mr. kraftwer1's solution worked for me (it's a hack, but will have to do until Apple fixes this). That is, adding... window.scrollTo(0, 0); after orientationChange works.

Finally, I just wanted to also mention that submitting additional bug reports on this issue to Apple will raise it's priority in their queue.

2
votes

The issue does indeed seem to be a bug in iOS 7.0 and 7.1 so far. I've only been able to reproduce it when using the device in landscape.

It happens in three situations that I know of, and in all cases the window.scrollTo(0, 0) hack resolves it.

  1. When rotating the device into landscape. Can be resolved with:

    $(window).on('orientationchange', function () {
      window.scrollTo(0, 0);
    });
    
  2. When dragging from the bottom of the document. Resolved by handling the scroll event:

    $(window).on('scroll', function () {
      var focusedElement;
    
      if ($(document).scrollTop() !== 0) {
        focusedElement = $(':focus');
        if (!(focusedElement.is('input') || focusedElement.is('textarea'))) window.scrollTo(0, 0);
      }
    });
    

    The exceptions for the focused elements are necessary because when the screen keyboard opens, it will also trigger a scroll event in the window.

  3. When closing the keyboard—then, all elements lose focus:

    formLayer.on('blur', 'input, textarea', function () {
      if (!$(':focus').length) window.scrollTo(0, 0);
    });
    

Since this issue is only relevant to iOS versions 7.0 and 7.1, it is better to limit this hack to the following expression:

var buggyIOS = /\b(?:iPhone|iPod|iPad).*?\bOS 7_[01]/.test(window.navigator.userAgent);
2
votes

Refining on the window.scrollTo(0,0) solution, I encapsulate in a self describing immediately invoked function expression and execute on document ready and window resize:

(function minimalUiFix() {
  var fix = function () {
    window.scrollTo(0,0);
  };

  $(fix);
  $(window).on('resize.minimal-ui-fix', fix);
})();

The benefit is that all the code related to the work around encapsulated together and the reason for the work around is described in the function name. This protects the rest of my beautiful code from being contaminated (too much) by strange work arounds.

There's a lot going on, but I'm using it in this jsbin.

This trick doesn't seem to do the trick for me. Check out this jsbin. What I have here is pretty simple: a fixed position container with overflow hidden that should take up the whole screen. Scrolling should not be possible, however, I'm still getting some unintended scrollable space. All this trick does is scroll back to the top. It doesn't eliminate the mysterious extra space that is causing the real problem.

But, applying the fix on scroll in addition to ready and resize seems to the be closest to decent work around I could find.

(function minimalUiFix() {

  var fix = function () { window.scrollTo(0,0); };
  $(fix);
  $(window).on('resize.minimal-ui-fix', fix);
  $(window).on('scroll.minimal-ui-fix', fix);

})();

I think this is the best we can hope for until Apple fixes Mobile Safari or another work around is discovered.

1
votes

The best fix I've found is here by Alexander Koleda.

window.addEventListener('scroll', function () {
    if (document.activeElement === document.body && window.scrollY > 0) {
        document.body.scrollTop = 0;
    }
}, true);

This also fixes the grey bar when you scroll to the bottom.

0
votes
<meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui=1, user-scalable=no">

Just remove the Minimal-UI, that got rid of it for me.

<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
-1
votes

I was reading this post trying to avoid the same issue and found another way to do so.

First let's detect if the device rotated (also we could detect later if is portrait or landscape), to do so I'm going to set up a var for checking mediaqueries from JS:

var mql = window.matchMedia("(orientation: portrait)");

Then we can listen if there is any change and rewrite the viewport:

    mql.addListener(function(m) {
        $('meta[name="viewport"]').attr("content","width=device-width, user-scalable=no, maximum-scale=1.2"); // No matter if is landscape or portrait
    });

Or we could be more specific and trigger something different for landscape/ portrait

    mql.addListener(function(m) {
        if(m.matches) {
            $('meta[name="viewport"]').attr("content","width=device-width, user-scalable=no, maximum-scale=1.2"); // Portrait
        }
        else {
            $('meta[name="viewport"]').attr("content","width=device-width, user-scalable=no, maximum-scale=1.0"); // Landscape
        }
    }); 

I hope this could help you!