166

I came across this problem a few times and was wondering if there was a solution to this problem. My problem occurs on the Chrome mobile app. There, you can scroll down a bit and the address bar disappears. So far, so good, let's make an example:
The container's height is set to 100vh.

How it looks with the address bar

As you can see, the bottom part gets cut off.

When I scroll down, it looks like this:

enter image description here

Now it looks good. So obviously Chrome calculates the address bar's height into the viewport height. So my question is:

Is there a way, that it looks the same with or without the address bar? So that the container expands or something?

danronmoon
  • 3,814
  • 5
  • 34
  • 56
Tobias Glaus
  • 3,008
  • 3
  • 18
  • 37
  • Only a script-solution could fix this. Read here: https://stackoverflow.com/questions/37112218/css3-100vh-not-constant-in-mobile-browser –  Oct 17 '18 at 07:01
  • without code it'll be difficult to help you.. – aflyzer Oct 17 '18 at 07:35
  • Maybe you should give a look at: https://developers.google.com/web/fundamentals/native-hardware/fullscreen/ and this question: https://stackoverflow.com/questions/28647604/force-hide-address-bar-in-chrome-on-android – Puka Oct 17 '18 at 07:39
  • 4
    Why don't you use height:100% instead of 100vh? The Chrome app returns the value including the address bar of height 100vh. – Andrew Li Mar 28 '19 at 09:50
  • 6
    @AndrewLi As you can see, there is a list. With 100%, the container wouldn't be full height. I'd have to set 100vh on the body height to make it work then, which resulted in the same problem. – Tobias Glaus Mar 28 '19 at 10:02
  • 3
    I have the same problem, but set the height in 100% doesn't fix it. – David Minaya May 15 '20 at 14:26
  • The event `visualViewport.resize` is fired while the url bar is appearing/disappearing, so you can bind to this one `visualViewport.addEventListener('resize', function() { })`. I haven't found a css only solution. https://stackoverflow.com/a/67947384/8941307 – Pieterjan Jul 03 '21 at 11:29
  • i solved like that: position: fixed; bottom:0; top:0; left:0; right:0; max-width: 100vw; – sawacrow Mar 18 '23 at 13:13

11 Answers11

146

As per this official article on Chrome web, the proper way to set the height to fill the visible viewport is with height: 100%, either on the <html> element or on a position: fixed element. As the document describes, this ensures compatibility with mobile Safari and is independent of how large the URL bar is.

Ross Light
  • 4,769
  • 1
  • 26
  • 37
  • 3
    Thank you very much. This article actually helped me understand what was happening behind the scene. So for my usecase, 100% seemed right, because it was a `position: fixed` element anyway. – Swashata Ghosh Jan 06 '20 at 15:30
  • 12
    It's important to note that if you still want to be able to facilitate scroll-toggling of the address bar visibility the height of the `` should still be set to `100vh` (or something greater than 100%) and only the target fill element should be assigned `position: fixed; height: 100%`. – Chunky Chunk Feb 10 '20 at 23:45
  • 3
    I have the same problem, but set the height in 100% doesn't fix it. – David Minaya May 15 '20 at 13:59
39

Try using min-height: -webkit-fill-available. You can also add it below height: 100vh as a fallback.

cnotethegr8
  • 7,342
  • 8
  • 68
  • 104
36

Update 2023

All modern browsers support CSS dvh units (dynamic view height).
100dvh equals 100% height of the visible area and dynamically changes when a mobile browser header or footer visibility is switched.

dvh showcase

The large, small, and dynamic viewport units article provides more details, and @jwseph correctly mentioned it in the answer.

An example of usage with a modal window and phone input to test a mobile keyboard:

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
}
.modal { 
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  
  height: 100vh; /* old browsers */  
  height: 100dvh; /* new browsers */
      
  width: 100%;
  
  padding: 16px;      
}

.modal__container {
    display: grid;
    grid-template-rows: 1fr auto;

    background: #2a9efa;
    height: 100%;
}

.modal__content {
  margin: 16px 0 0 16px;
  padding-right: 16px;
  
  height: 100%;
  overflow-y: scroll;
}

.modal__footer {
  padding: 8px 24px;
  background: black;
}

.long-text {
   min-height: 1800px;
   background: yellow;
}
<div class="modal">
  <div class="modal__container">
     <div class="modal__content">
        <label>Phone:</label>
        <input type="tel" placeholder="+1 000">
  
        <p class="long-text">Long text</p>   
    </div> 
    <div class="modal__footer">
       <button>Close</button>
    </div>    
  </div>  
</div>

Previous answer

The community still has no strict agreement on how browsers should behave with the movement of top, bottom, and side panels from the developers' point of view.

The mentioned problem in the question is well known:

enter image description here

  1. It all started with the Apple Webkit Issue. One of the problems was that website developers used vh for the calculation of the font size (calc(100 / vh * something)). If 100vh would be dynamic, when a user scrolls down and the address bar is hidden, then font size, as with any other bound elements, will be distorted, producing a very bad user experience, not to mention being CPU/GPU intensive task.
    Apple's decision was to match the larger size of the screen (without the address bar) to 100vh constantly. So, when the address bar is displayed, and you use 100vh height, the bottom part will go out of the screen. Many developers do not agree with that decision and consider viewport units to be dynamic and exactly equal to the visible "view port".

  2. The Google Chrome team decided to be compatible with the Apple browser and stuck to the same decision.

  3. height: 100% in most modern browsers is equal to the real visible part, i.e., the height varies and depends on whether the address bar is visible or hidden during the scroll.

  4. Bars can appear not only on the top of the screen but also at the bottom (modern iOS), as well as an onscreen keyboard can make the view shorter. There is a nice demo to check in mobile devices the actual size of 100vh vs 100%.
    enter image description here


Solution 1

html, body { height: 100%; }
.footer-element { 
  position: fixed; 
  bottom: 10px;
}

Solution 2
Compensate some dependency on the vh with the visible bar height equal to the "100vh - 100%", when the bar is hidden the difference will be 0.

html, body { height: 100vh; }
.footer-element { 
  position: fixed; 
  bottom: calc(10px + (100vh - 100%));
}
Artur A
  • 7,115
  • 57
  • 60
  • 3
    Please be aware that Mobile Operating Systems have different behaviors when a mobile keyboard is opened. The above solutions do not cover that topic. – Artur A Jun 05 '22 at 17:19
  • Does 100vh include the address bar/all bars on all major browsers in all environments? (Say, Chrome, Firefox, Safari, or Edge on any operating system). – Kevin Wheeler Aug 15 '23 at 15:28
  • (100vh - 100%) didn't work for me! – Mehdi Aug 18 '23 at 04:58
15
.my-element {
  height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}

Now let’s get the inner height of the viewport in JavaScript:

// First we get the viewport height and we multiple it by 1% to get a value for a vh unit
let vh = window.innerHeight * 0.01;
// Then we set the value in the --vh custom property to the root of the document
document.documentElement.style.setProperty('--vh', `${vh}px`);

source: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

Mahdi Bashirpour
  • 17,147
  • 12
  • 117
  • 144
10

you can fix the address bar issue with setting height: 100% on html and body tag and off course set margin and padding of body to zero and also you can handle scrolling in your main div for better controll

Babak Zarrinbal
  • 101
  • 1
  • 4
7

There are now three new viewport units:

  • sv* - Smallest possible viewport, the size without the address bar
  • lv* - Largest possible viewport, the size including the address bar
  • dv* - Dynamic viewport, the size depending on whether the address bar is visible

Usage

Since you want the container's size to change depending on whether the address bar is visible, the unit you want is dvh.

.container {
    height: 100dvh;
}

Hope this helped!

Further reading

jwseph
  • 126
  • 1
  • 5
4

I just figured out a way how to resize the element so that the height doesn't include the android home-button-less smartphones with the onscreen-navbar AND the browser top bar. If the content is bigger than the screen the element should grow to the size it can fit everything, that's why I am using min-height.


EDIT:

Added a snippet using a class instead of changing the styling in JS

// save old window size to adjust only if width changed
let oldWidth = window.innerWidth,
  oldHeight = window.innerHeight;
// element to adjust
const target = document.querySelector(".vh100");
// adjust the size if window was resized
window.addEventListener("resize", handleResize);

function handleResize(initial = false) { // the parameter is used for calling the function on page load
  /*
   * if the width changed then resize
   * without this Chrome mobile resizes every time navbar is hidden
   */
  if (window.innerWidth !== oldWidth || initial) {
    // stretch the target
    target.classList.add("setting-100vh");
    // save height and apply as min height
    const h = target.clientHeight;
    target.classList.remove("setting-100vh");
    target.style.minHeight = h + "px";
  }
}
// call when page is loaded
handleResize(true);
* {
  margin: 0;
}

.vh100 {
  background-color: green;
}


/*
* Stretch the element to window borders and save the height in JS
*/

.setting-100vh {
  position: fixed;
  top: 0;
  bottom: 0;
  min-height: unset;
}
<body>
  <header class="vh100">
    <h1>100vh on mobile</h1>
  </header>
  <main>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Possimus ipsa officia mollitia facilis esse cupiditate, nisi recusandae quas id enim alias eaque suscipit voluptates laudantium quasi saepe deserunt labore fuga deleniti placeat, necessitatibus
      quibusdam. Quaerat adipisci provident minima laboriosam modi ullam accusamus error dolores iure ducimus laborum similique distinctio temporibus voluptas nulla quod ipsa, nostrum quam cumque id animi unde consectetur incidunt! Dolorem sed quisquam
      at cumque. Cumque non nam exercitationem corporis? Minus sed explicabo maiores ipsam ratione. Quam, fugit asperiores nesciunt dolores culpa, numquam blanditiis sint dolorum ex corrupti illo veniam nostrum odio voluptatibus accusantium ullam impedit
      eligendi voluptates?</p>
  </main>
</body>
danronmoon
  • 3,814
  • 5
  • 34
  • 56
Andris Jefimovs
  • 689
  • 6
  • 17
4

I ran into a similar problem and used this solution with ReactJS:

import { useLayoutEffect, useState } from 'react';

function useWindowSize() {
  const [size, setSize] = useState([0, 0]);
  useLayoutEffect(() => {
    function updateSize() {
      setSize([window.innerWidth, window.innerHeight]);
    }
    window.addEventListener('resize', updateSize);
    updateSize();
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  return size;
}

This useWindowSize function is taken from Rerender view on browser resize with React.

When I used it in my code, it looked like this:

const MessageList = () => {
  const { messages } = useContext(ChatContext);
  const [, windowHeight] = useWindowSize();
  return (
    <div
      className="messages"
      style={{
        height: windowHeight - 80 - 48, // window - message form - navbar
      }}
    >
      {messages.map((m, i, list) => ( <Message ... /> )}
    </div>
  );
};
thiagobraga
  • 1,519
  • 2
  • 15
  • 29
Itays2005
  • 65
  • 1
  • 2
1

Try Using Dynamic View Height(dvh) e.g 100dvh Its a relatively new property and some browsers might not support it still but it solves the issue of the address bar.

https://dev.to/frehner/css-vh-dvh-lvh-svh-and-vw-units-27k4

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 31 '23 at 13:38
  • Not everyone will extrapolate what you are saying or follow the link, so it's best to show an example declaration such as `height: 100dvh;` – Tim R Jun 01 '23 at 06:58
0

Just wanted to expand a little bit on the top answer here-- I found that as Ross Light mentioned above you want to use height: 100% to account for the web browser's address bar. However, for this to work you have to set set the height for the html tag and body tag equal to height: 100% or your divs will not expand properly:

<!DOCTYPE html>
<html>
  <head>

    <style>

      html, body {
        height: 100%;
      }

      .fillViewport {
        height: 100%;
      }

      .redBackground {
        background-color: red;
      }

    </style>

  </head>
  <body>

    <div class="fillViewport redBackground"></div>

  <body>
</html>
topherPedersen
  • 541
  • 7
  • 15
0

The following snippet worked for me. I inserted a border in the main tag as an example of a wrapper.

* {
  margin: 0;
  border: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  position: fixed;
  padding: 0.5rem;
  height: 100%;
  width: 100%;
}

body {
  position: relative;
  height: 100%;
  width: 100%;
}

main {
  position: relative;
  border: 1px solid black;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}
Dimi
  • 26
  • 2