This article was written in 2014. It might or it might not be outdated. And it could be that the layout breaks. If that’s the case please let me know.

Test if the vh-unit is implemented crazily

I love the vh unit. You can use it to make something as high as the viewport, or you can use it to make sure that something is never as high as the viewport. There’s lots of other stuff you can do with it as well. I love it, and I want to use it for every project I work on. Unfortunately there’s an insane bug in Safari on iOS that makes it impossible to use it. I tried to fix it with a little script, but I think I can use a little help to improve it.

Crazy, buggy implementation

In Safari on iOS 7 100vh is as high as the viewport as long as the document is not higher than the viewport. If the document is higher than the viewport, 100vh is as high as the document. Which is insane. This is solved in iOS8.

The solution

I wrote this simple script that detects if the browser of the visitor has this bug. If so, it adds the class crazy-vh to the HTML element. Here’s a live example. If you place this script right before the closing body tag of your document you can provide an alternative style for those poor people with Safari on iOS. If you have control over the back-end you can improve the performance of this script by setting a cookie and adding the className on the server.

The previous version of this script was unreliable. Matthijs Brouwer suggested me to use document.readyState instead of a simple setTimeout. Which works perfectly. The problem is, of course that this can result in the layout changing after the script runs. I don’t know how to solve this, how to run this script reliably before the styles are loaded. If you do, and you know how to solve it, please let me know.

An alternative solution

Stephen Hay pointed me in the direction of this pixel perfect buggyfill of this bug by Rodney Rehm, which converts all vh occurrences in your stylesheets to pixel values.

Comments

  1. Interesting (read: stupid) bug.

    Trying to run the script before `window.onload` is going to be a problem, seeing how (the `vh`) CSS will probably influence the window height.

    But your issue is not with ‘feature-detecting’ the `vh` unit, right? It’s with providing a fallback for iOS. Maybe this is a place for ‘browser sniffing’? Or deal with the compromise of a ‘layout re-draw’… :(

    Anyway, not a direct answer to your question but a couple of thoughts after taking a quick look at your script:

    – Why do you need `a` at all? You simply compare the height of `b` with the viewport, right?
    – I also don’t understand the need for a timeout (`interval` in your case). If you want to run async there are other ways and you will not prevent a potential layout redraw anyway, it seems.
    – Maybe take a look at the Modernizr test for the vh-unit
    – I assume you’ve seen this bugreport in ‘Device Bugs’ and Modernizr issue?

    • Vasilis
    • #

    Thanks for your reply, David. Lots of questions to answer. First of all, I need `a` in my script to make sure that the content of the document is higher than the viewport. If I didn’t create `a` it could be very well possible that the bug is not detected. This could be a problem when the content of the page changes.

    I’ve considered using browser sniffing, but I really don’t know what browser to target, let alone how. Do I need to target only this version of Safari on iOS? Will it be fixed in next versions? If so, can I be bothered to revisit my script? I think feature detection is the right way to go here, since I don’t know if it’s only an issue on iOS. Could be other webkits as well.

    About the timeout: I’d prefer not to use it. But without it the bug is not detected on iOS. I tried several ways of making this work as a simple Modernizr like script in the head, but alas: the bug was not always detected, which I think is terrible.

    The current Modernizr test only tests if the vh unit is supported. It doesn’t check if the implementation is broken. I need to test that.

    And at last, the end of my reply: I didn’t know those links you posted. Thanks!

  2. Thanks Vasilis. I was commenting in a hurry and could have thought more on it before brainfarting all over your site ;)

    `a` is needed to provide content (‘height’) for the document. Understood (although I wonder if it really is :P ).
    Browser Sniffing. Obviously a generic ‘workaround’ should be aimed for.
    `setTimeout`: I wonder if it is still needed if you run the script `window.onload` or place it before `</body>`? Is the bug occurance not directly linked to the moment the page can be layed out? (You would still have the potential layout change)
    I am aware Modernizr tests for support and not functionality, but I wondered if their approach of checking `computedStyle` (and `50vh` offered benefits over your check on `clientHeight`…

    • Vasilis
    • #

    `a` is needed to provide content (‘height’) for the document. Understood (although I wonder if it really is :P ).

    It is, I tried (-:

    `setTimeout`: I wonder if it is still needed if you run the script `window.onload` or place it before ``? Is the bug occurance not directly linked to the moment the page can be layed out? (You would still have the potential layout change)

    We’re talking about a weird bug in a weird browser here. I tried all kinds of versions of this script. The problem is that it takes a little time to be able to detect the bug. It doesn’t occur right away, both divs need a little time to grow to their final height. I used a simple timeout of one millisecond, but those results were unreliable. It worked sometimes. Simply placing it at the bottom doesn’t change this. But I’d love to be wrong here. Please do teach me a faster way to do this (-:

    And finally, I tried computedStyle and it doesn’t make much of a difference. It adds a unit to the end which makes it harder to compare, that’s why I chose clientHeight. But again, I’m a n00b in this matter and I’d love to be proven wrong.