By bitinn


2014-07-22 13:43:42 8 Comments

(This is a multi-part question, I will try my best to summarise the scenario.)

We are currently building a responsive web app (news reader) that allow users to swipe between tabbed content, as well as scroll vertically inside each tabbed content.

A common approach to the problem is to have a wrapper div that fills the browser viewport, set overflow to hidden or auto, then scroll horizontally and/or vertically inside it.

This approach is great but has one main drawback: since the height of the document is exactly the same as the browser viewport, the mobile browser will not hide the address bar/navigation menu.

There are numerous hacks and viewport properties that enable us to get more screen real estate, but none are quite as effective as minimal-ui (introduced in iOS 7.1).

News came yesterday that iOS 8 beta4 had removed minimal-ui from Mobile Safari (see Webkit section in iOS 8 Release Notes), which left us wondering:

Q1. Is it still possible to hide the address bar on Mobile Safari?

As far as we know, iOS 7 no longer responds to the window.scrollTo hack, this suggests we have to live with the smaller screen space, unless we adopt a vertical layout or use mobile-web-app-capable.

Q2. Is it still possible to have a similar soft fullscreen experience?

By soft fullscreen I really mean without using the mobile-web-app-capable meta tag.

Our web app is built to be accessible, any page can be bookmarked or shared using the native browser menu. By adding mobile-web-app-capable we prevent users from invoking such menu (when it's saved to homescreen), which confuses and antagonises users.

minimal-ui used to be the middle-ground, hiding the menu by default but keeping it accessible with a tap -- though Apple might have removed it due to other accessibility concerns (such as users not knowing where to tap to activate the menu).

Q3. Is a fullscreen experience worth the trouble?

It would seem that a fullscreen API is not coming to iOS anytime soon, but even if it is, I don't see how the menu will be kept accessible (same goes for Chrome on Android).

In this case, maybe we should just leave mobile safari as it is, and account for viewport height (for iPhone 5+, it's 460 = 568 - 108, where 108 includes the OS bar, address bar and navigation menu; for iPhone 4 or older, it's 372).

Would love to hear some alternatives (besides building a native app).

9 comments

@ganar 2019-05-09 20:26:46

It is possible to get a web application running in full screen in both iOS and Android, it is called a PWA and after mucha hard work, it was the only way around this issue.

PWAs open a number of interesting options for development that should not be missed. I've made a couple already, check out this Public and Private Tender Manual For Designers (Spanish). And here is an English explanation from the CosmicJS site

@Code Freeze 2018-09-22 22:25:33

It IS possible, using something like the below example that I put together with the help of work from (https://gist.github.com/bitinn/1700068a276fb29740a7) that didn't quite work on iOS 11:

Here's the modified code that works on iOS 11.03, please comment if it worked for you.

The key is adding some size to BODY so the browser can scroll, ex: height: calc(100% + 40px);

Full sample below & link to view in your browser (please test!)

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CodeHots iOS WebApp Minimal UI via Scroll Test</title>

    <style>
        html, body {
            height: 100%;
        }
        html {
            background-color: red;
        }
        body {
            background-color: blue;
            /* important to allow page to scroll */
            height: calc(100% + 40px);
            margin: 0;
        }
        div.header {
            width: 100%;
            height: 40px;
            background-color: green;
            overflow: hidden;
        }
        div.content {
            height: 100%;
            height: calc(100% - 40px);
            width: 100%;
            background-color: purple;
            overflow: hidden;
        }
        div.cover {
            position: absolute;
            top: 0;
            left: 0;
            z-index: 100;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: rgba(0, 0, 0, 0.5);
            color: #fff;
            display: none;
        }
        @media screen and (width: 320px) {
            html {
                height: calc(100% + 72px);
            }
            div.cover {
                display: block;
            }
        }
    </style>
    <script>
        var timeout;

        function interceptTouchMove(){
            // and disable the touchmove features 
            window.addEventListener("touchmove", (event)=>{
                if (!event.target.classList.contains('scrollable')) {
                    // no more scrolling
                    event.preventDefault();
                }
            }, false); 
        }

        function scrollDetect(event){
            // wait for the result to settle
            if( timeout ) clearTimeout(timeout);

            timeout = setTimeout(function() {
                console.log( 'scrolled up detected..' );
                if (window.scrollY > 35) {
                    console.log( ' .. moved up enough to go into minimal UI mode. cover off and locking touchmove!');
                    // hide the fixed scroll-cover
                    var cover = document.querySelector('div.cover');
                    cover.style.display = 'none';

                    // push back down to designated start-point. (as it sometimes overscrolls (this is jQuery implementation I used))
                    window.scrollY = 40;

                    // and disable the touchmove features 
                    interceptTouchMove();

                    // turn off scroll checker
                    window.removeEventListener('scroll', scrollDetect );                
                }
            }, 200);            
        }

        // listen to scroll to know when in minimal-ui mode.
        window.addEventListener('scroll', scrollDetect, false );
    </script>
</head>
<body>

    <div class="header">
        <p>header zone</p>
    </div>
    <div class="content">
        <p>content</p>
    </div>
    <div class="cover">
        <p>scroll to soft fullscreen</p>
    </div>

</body>

Full example link here: https://repos.codehot.tech/misc/ios-webapp-example2.html

@Stephen Garside 2016-07-05 05:15:04

The easiest way I found to fix this was to set the height of the body and html elements to 100.1% for any request where the user agent was an iphone. This only works in Landscape mode, but thats all I needed.

html.iphone, 
html.iphone body { height: 100.1%; }

Check it out at https://www.360jungle.com/virtual-tour/25

@Téwa 2017-12-21 15:26:37

Thanks @Stephen. height: 100.1% helped me. However, when I opened 360jungle.com/virtual-tour/25 on iPhone(iOS 11.1.1) Safari and clicked the buttons in the bottom, the address and tool bar appeared. This is because the buttons are too close to the end of display. I guess it would be better move them somewhere else on mobile mode.

@Francesco Frapporti 2014-09-26 17:51:11

Just say goodbye to minimal-ui (for now)

It's true, minimal-ui could be both useful and harmful, and I suppose the trade-off now has another balance, in favor of newer, bigger iPhones.

I've been dealing with the issue while working with my js framework for HTML5 apps. After many attempted solutions, each with their drawbacks, I surrendered to considering that space lost on iPhones previous than 6. Given the situation, I think that the only solid and predictable behavior is a pre-determined one.

In short, I ended up preventing any form of minimal-ui, so at least my screen height is always the same and you always know what actual space you have for your app.

With the help of time, enough users will have more room.


EDIT

How I do it

This is a little simplified, for demo purpose, but should work for you. Assuming you have a main container

html, body, #main {
  height: 100%;
  width: 100%;
  overflow: hidden;
}
.view {
  width: 100%;
  height: 100%;
  overflow: scroll;
}

Then:

  1. then with js, I set #main's height to the window's available height. This also helps dealing with other scrolling bugs found in both iOS and Android. It also means that you need to deal on how to update it, just note that;

  2. I block over-scrolling when reaching the boundaries of the scroll. This one is a bit more deep in my code, but I think you can as well follow the principle of this answer for basic functionality. I think it could flickr a little, but will do the job.


See the demo (on a iPhone)

As a sidenote: this app too is bookmarkable, as it uses an internal routing to hashed addresses, but I also added a prompt iOS users to add to home. I feel this way helps loyalty and returning visitors (and so the lost space is back).

@Steven Palinkas 2014-11-21 09:34:23

To disable minimal-ui seems very reasonable to me. Please, include short description for how to do that!

@Francesco Frapporti 2014-11-21 13:30:24

You're right, I added a little how-to. Many other ways will work.

@dmr07 2015-09-24 21:37:05

Your demo doesn't work on iOS8, according to my iPhone 5.

@Francesco Frapporti 2015-09-30 11:33:08

Thank you for letting me know, it must be some update because it used to work. Are you in safari? What do you mean exactly with it doesn't work?

@Gajus 2014-11-12 10:22:53

The minimal-ui viewport property is no longer supported in iOS 8. However, the minimal-ui itself is not gone. User can enter the minimal-ui with a "touch-drag down" gesture.

There are several pre-conditions and obstacles to manage the view state, e.g. for minimal-ui to work, there has to be enough content to enable user to scroll; for minimal-ui to persist, window scroll must be offset on page load and after orientation change. However, there is no way of calculating the dimensions of the minimal-ui using the screen variable, and thus no way of telling when user is in the minimal-ui in advance.

These observations is a result of research as part of developing Brim – view manager for iOS 8. The end implementation works in the following way:

When page is loaded, Brim will create a treadmill element. Treadmill element is used to give user space to scroll. Presence of the treadmill element ensures that user can enter the minimal-ui view and that it continues to persist if user reloads the page or changes device orientation. It is invisible to the user the entire time. This element has ID brim-treadmill.

Upon loading the page or after changing the orientation, Brim is using Scream to detect if page is in the minimal-ui view (page that has been previously in minimal-ui and has been reloaded will remain in the minimal-ui if content height is greater than the viewport height).

When page is in the minimal-ui, Brim will disable scrolling of the document (it does this in a safe way that does not affect the contents of the main element). Disabling document scrolling prevents accidentally leaving the minimal-ui when scrolling upwards. As per the original iOS 7.1 spec, tapping the top bar brings back the rest of the chrome.

The end result looks like this:

Brim in iOS simulator.

For the sake of documentation, and in case you prefer to write your own implementation, it is worth noting that you cannot use Scream to detect if device is in minimal-ui straight after the orientationchange event because window dimensions do not reflect the new orientation until the rotation animation has ended. You have to attach a listener to the orientationchangeend event.

Scream and orientationchangeend have been developed as part of this project.

@bitinn 2014-11-12 16:05:58

This is way more expansive than my original answer, marked as new answer until even better solution arrives :)

@INT 2014-11-22 23:52:24

Seems nice! Can I force minimal-ui without the initial scroll?

@Gajus 2014-11-24 06:05:48

@INT, not that I know of. There used to be a way using programmatic scrolling, but it does not work anymore.

@Petr Urban 2015-01-21 17:11:06

This really is never ending story. I'm a game developer in HTML and minimal-ui in iOS 7.1 worked just fine - it was the only way to have an app running fullscreen AND at the same time being able to touch in the bottom of the screen. Solutions with page swiping are nice, but not good enough :( Apple, we need proper implementation of full screen API for games, please.

@Jose Browne 2015-08-13 23:49:19

@Petr: I couldn't agree more. When This fix was announced in 7.1 we quickly rolled out a new checkout purchase flow which pinned the primary CTA to the bottom of the screen.. this worked and converted great! It felt very "native" and seamless. Which i think is the exact problem. If you think about it, it's not in Apples' best interest for web apps to feel native. It is in fact a direct conflict of interest with their App Store monopoly. This is, IMO, the only valid reason why a feature that makes so much sense was fixed and then intentionally removed. #my2Cents :)

@Patrick Gunderson 2015-09-21 23:57:45

@PetrUrban I am convinced that Apple would prefer you to publish your game as a phonegap app than allow you to serve over the web. Their recent decision to allow ad blockers for safari cements this idea.

@JonnieJS 2015-12-01 07:45:35

@GajusKuizinas actually, in ios9.2(iphone5s - but i guess more devices) I can detect the minimal-ui state: window.innerHeight will have different value than document.documentElement.clientHeight.

@Gajus 2015-12-01 11:09:20

@JonnieJS You still want to support older phone and this is not all Scream does – it also forces user to stay in fullscreen mode.

@zur4ik 2016-02-04 12:40:59

@GajusKuizinas Thanks for solution. It works for me only in Safari, not in Chrome. Do you expect same or it's just in my case?

@Gajus 2016-02-04 14:16:53

@zur4ik No, Chrome is not supported. See this thread for more details github.com/gajus/scream/issues/1

@Razor 2014-09-04 11:37:23

The root problem here seems that iOS8 safari won't hide the address bar when scrolling down if the content is equal or less than the viewport.

As you found out already, adding some padding at the bottom gets around this issue:

html {
    /* enough space to scroll up to get fullscreen on iOS8 */
    padding-bottom: 80px;
}
// sort of emulate safari's "bounce back to top" scroll
window.addEventListener('scroll', function(ev) {
    // avoids scrolling when the focused element is e.g. an input
    if (
        !document.activeElement
        || document.activeElement === document.body
    ) {
        document.body.scrollIntoViewIfNeeded(true);
    }
});

The above css should be conditionally applied, for example with UA sniffing adding a gt-ios8 class to <html>.

@Ben Sinclair 2014-10-26 23:02:47

What does this JS exactly do?

@Gajus 2014-11-04 19:29:14

If you are referring to scrollIntoViewIfNeeded, it is a non standard derivation of scrollIntoView (developer.mozilla.org/en-US/docs/Web/API/Element.scrollInto‌​View). As the name implies, the method scrolls the element into the view. true parameter says to align the view with the top of the element. This in effect should prevent you from scrolling. The implementation is flawed though.

@bitinn 2014-08-01 11:18:34

Since there is no programmatic way to mimic minimal-ui, we have come up with a different workaround, using calc() and known iOS address bar height to our advantage:

The following demo page (also available on gist, more technical details there) will prompt user to scroll, which then triggers a soft-fullscreen (hide address bar/menu), where header and content fills the new viewport.

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Scroll Test</title>

    <style>
        html, body {
            height: 100%;
        }

        html {
            background-color: red;
        }

        body {
            background-color: blue;
            margin: 0;
        }

        div.header {
            width: 100%;
            height: 40px;
            background-color: green;
            overflow: hidden;
        }

        div.content {
            height: 100%;
            height: calc(100% - 40px);
            width: 100%;
            background-color: purple;
            overflow: hidden;
        }

        div.cover {
            position: absolute;
            top: 0;
            left: 0;
            z-index: 100;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: rgba(0, 0, 0, 0.5);
            color: #fff;
            display: none;
        }

        @media screen and (width: 320px) {
            html {
                height: calc(100% + 72px);
            }

            div.cover {
                display: block;
            }
        }
    </style>
    <script>
        var timeout;

        window.addEventListener('scroll', function(ev) {

            if (timeout) {
                clearTimeout(timeout);
            }

            timeout = setTimeout(function() {

                if (window.scrollY > 0) {
                    var cover = document.querySelector('div.cover');
                    cover.style.display = 'none';
                }

            }, 200);

        });
    </script>
</head>
<body>

    <div class="header">
        <p>header</p>
    </div>
    <div class="content">
        <p>content</p>
    </div>
    <div class="cover">
        <p>scroll to soft fullscreen</p>
    </div>

</body>
</html>

@arcom 2017-07-22 13:34:52

This demo doesn't seem to work on my iphone 7

@besserwisser 2017-09-07 12:29:20

Demo does not work on iPhone 5 iOS 10.3

@scooterlord 2014-07-30 23:23:06

I want to comment/partially answer/share my thoughts. I am using the overflow-y:scroll technique for a big upcoming project of mine. Using it has two MAJOR advantages.

a) You can use a drawer with action buttons from the bottom of the screen; if the document scrolls and the bottom bar disappears, tapping on a button located at the bottom of the screen will first make the bottom bar appear, and then be clickable. Also, the way this thing works, causes trouble with modals that have buttons at the far bottom.

b) When using an overflown element, the only things that are repainted in case of major css changes are the ones in the viewable screen. This gave me a huge performance boost when using javascript to alter css of multiple elements on the fly. For example, if you have a list of 20 elements you need repainted and only two of them are on-screen in the overflown element, only those are repainted while the rest are repainted when scrolling. Without it all 20 elements are repainted.

..of course it depends on the project and if you need any of the functionality I mentioned. Google uses overflown elements for gmail to use the functionality I described on a). Imo, it's worth the while, even considering the small height in older iphones (372px as you said).

@iFeli 2014-07-22 13:50:04

I haven't done web design for iOS but from what I recall seeing in the WWDC sessions and in documentation, the search bar in Mobile Safari, and navigation bars across the OS, will now automatically resize and shrink to show more of your content.

You can test this in Safari on an iPhone and notice that, when you scroll down to see more contents on a page, the navigation/search bar is hidden automatically.

Perhaps leaving the address bar/navigation bar as is, and not creating a full-screen experience, is what's best. I don't see Apple doing that anytime soon. And at most they are not automatically controlling when the address bar shows/hides.

Sure, you are losing screen real estate, specially on an iPhone 4 or 4S, but there doesn't seem to be an alternative as of Beta 4.

@bitinn 2014-07-22 13:52:11

I knew this feature of iOS7+, but see my explanation above: since the height of document is exactly the same as browser viewport, mobile browser will not hide address bar/navigation menu, as no scroll takes place on document-level.

@iFeli 2014-07-22 13:56:45

This may be something of a limitation now that Beta 4 has removed that feature. It's possible and likely that Apple is controlling the address bar automatically and keeping developers from accessing it.

@ProblemsOfSumit 2014-10-01 10:33:25

I haven't done web design for iOS - if you're doing web design, you do it for every platform. Because the web is on every platform.

@iFeli 2014-10-01 17:22:20

@Sumit I know that working on web is universal, but each browser and their underlying frameworks have specific CSS attributes. So Chrome may have some attributes unavailable to Safari and FireFox, and viceversa.

Related Questions

Sponsored Content

41 Answered Questions

[SOLVED] How do I remove a property from a JavaScript object?

6 Answered Questions

15 Answered Questions

[SOLVED] CSS3 100vh not constant in mobile browser

4 Answered Questions

19 Answered Questions

[SOLVED] Background image jumps when address bar hides iOS/Android/Mobile Chrome

3 Answered Questions

3 Answered Questions

Sponsored Content