Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 13 Safari: Bug(s) in window.innerHeight

As I understand it, the window.innerHeight should return the size of the viewport without the browser chrome (address bar, navigation, tabs, etc.). But this doesn't seem to be the case in the latest version of iOS13. Instead there are two problems:

  1. (Sometimes* too small in portrait) If you rotate from portrait mode to landscape mode with no tabs open and then back to portrait mode, the window.innerHeight value ends up being too small (by about the size of the bottom navigation bar) giving this horrible white bar at the bottom of the screen. See this discussion on macrumors for more details: https://forums.macrumors.com/threads/is-this-a-mobile-safari-bug-white-space-appears-at-bottom-after-rotating-iphone.2209551/
  2. (Sometimes* too big in landscape) If you have a bunch of tabs open, "Show tab bar" turned on and then rotate from portrait mode into landscape mode, then window.innerHeight is too big and the bottom of the screen gets cut off.

Even after turning on every conceivable viewport tag and all permutations thereof, it doesn't seem to work. I've also looked at several "tutorials" on how to handle this problem in iOS Safari, and to date every one that I've checked is broken.

I've also tried all variations of the window.innerHeight, with more or less the same result:

  • The new visual viewport API returns the same results, no different than window.innerHeight. Bottom is still truncated in landscape with tab bar and portrait mode still has the white bar at the bottom.
  • document.documentElement.clientHeight with various permutations of CSS (using 100vh, 100%, etc.) gives the same result. Ditto for getBoundingClientRect on various divs and combinations of div elements.
  • window.outerHeight and screen.height give the size of the full screen without browser chrome, which is generally too big and causes an overflow.
  • Also tried a bunch of other random things that I've forgotten by now (should have taken notes).

You can manually fudge it on a per-device basis if you can guess the size of the top and bottom browser chrome, but this is extremely fragile. I'm looking for a solution that doesn't involve building a giant look up table of every iOS device and software configuration.

I'm trying to make a fullscreen canvas element for a web game and this issue is blocking my ability to ship. As far as I know this issue is only present in iOS13. After looking around for weeks I still haven't found a good fix.

like image 852
Mikola Avatar asked Nov 07 '22 08:11

Mikola


1 Answers

I have had the same issue recently and I was able to solve it like this:

CSS (only relevant parts shown):

html {
    height: 100%;
    min-height: 100%;
    max-height: 100%;
    background: #99f; /* Safari for iOS and Opera for Android in fullscreen mode?!?! */
}

body {
    padding: 0;
    margin: 0;
    color: #000;
    width: 100%; /* I was desperate! This was a wild guess... And worked! */
    height: 100%;
    min-height: 100%;
    max-height: 100%;
    overflow: hidden;
    background: #99f;
}

TypeScript (only relevant parts shown):

// Assume everything here is in the global scope

function detectIOSOrSafari(): boolean {
    // https://stackoverflow.com/q/9038625/3569421
    if ((navigator.userAgent.indexOf("Chrome") <= 0 && navigator.userAgent.indexOf("Safari") >= 0) ||
        (navigator.userAgent.indexOf("Mac") >= 0 && ("ontouchend" in document)))
        return true;
    switch (navigator.platform) {
        case "iPad Simulator":
        case "iPhone Simulator":
        case "iPod Simulator":
        case "iPad":
        case "iPhone":
        case "iPod":
            return true;
    }
    return false;
}

const isIOSOrSafari = detectIOSOrSafari();

function adjustWindowSize(): void {
    let widthCss = window.innerWidth,
        heightCss = window.innerHeight;

    if (document.documentElement && ("clientWidth" in document.documentElement)) {
        widthCss = document.documentElement.clientWidth;
        heightCss = document.documentElement.clientHeight;
    }

    if (isIOSOrSafari) {
        let bodyRect: DOMRect = null;

        // Another act out of desperation...
        if (document.documentElement && ("getBoundingClientRect" in document.documentElement))
            bodyRect = document.documentElement.getBoundingClientRect();
        else if (("getBoundingClientRect" in document.body))
            bodyRect = document.body.getBoundingClientRect();

        if (bodyRect) {
            widthCss = bodyRect.right - bodyRect.left;
            heightCss = bodyRect.bottom - bodyRect.top;
        }
    }

    // Rest of the code, where I use widthCss and heightCss to compute my canvas' size
}

window.onresize = adjustWindowSize;

You can check out the entire source code in the project's repository: https://github.com/carlosrafaelgn/pixel

like image 100
carlosrafaelgn Avatar answered Nov 15 '22 00:11

carlosrafaelgn