An explainer page and demo of how the URL bar interacts with pages on the web
The mobile browser URL bar is such a pain! It interacts with the page in a different way in every browser. This page hopes to explain the nuances of how the URL bar affects the web page, document the differences between the browsers, and explain some ~~proposed~~ recent changes to Chrome to improve the current situation.
I've also provided a demo page to demonstrate the issues. This page can be used to compare the behavior between different browsers.
Note: These URL bar issues are inherent only to mobile platforms so all the above discussion implicitly applies only to the mobile versions of each browser.
Caveat: ~~I have not had the chance to try out Edge browser on mobile so it's not included below.~~ Edge does not have a movable URL bar.
This change shipped in Chrome M56. I've preserved the rest of this explainer for reference but here's the distilled details of what developers should know:
width: 100%; height: 100%, its height is calculated from the ICB. Prior to this change, when the user hides the URL bar, Chrome would resize the ICB to fit the new visible area. With this change, the ICB will not change height in response to the URL bar. It will remain fixed to the size it would be when the URL bar is showing.
vhunits are used to size elements on the page with respect to the "viewport" height.
100vhmeans use 100% of the viewport height. Prior to this change, Chrome would take "viewport" to mean ICB, which also meant these elements would get resized if the URL bar was hidden or shown. With this change,
100vhwill remain fixed to the height it would have been when the URL bar was hidden. This is different from ICB height to match Safari's behavior.
position: fixedelements will continue to get their height from the dynamic visual viewport. That is, hiding/showing the URL bar will resize
position: fixedelements with percentage height.
That is, there are three viewports used for sizing elements:
The "vh viewport" is used to size
vhunits. It doesn't change height in response to the URL bar and behaves as if the URL bar is always hidden.
The "visual viewport" is used as the root container for
position: fixedelements. It is resized in response to the URL bar.
The "initial containing block" is used as the root container for all elements on the page other than
positoin: fixed. It doesn't change height in response to the URL bar and behaves as if the URL bar is always showing.
display:noneon the content while the overlay is up. This isn't seen in Safari since it's more aggressive about showing the URL bar. See this demo page displaying the issue. The solution is to make the overlay
Browsers will fire a resize event on the window when it changes dimensions. On desktops, this would happen when the user resizes the browser window. On mobile, this can happen when the URL bar is shown/hidden or when the user rotates their device.
So in terms of the URL bar hiding/showing, when is a resize event fired?
Simply speaking, the Initial Containing Block (I'll use ICB for short) is the root block used for sizing non-position:fixed elements on the page. That is, if an element specifies a percentage based size, it must use the computed height of one of its ancestors. But what about the root element? It uses the ICB. According to the spec, the ICB has the same dimensions as the viewport. However, this is a little vague as there are now a few notions of "viewport".
So, how does the URL bar affect the ICB in each browser?
According to the spec, the containing block for position: fixed elements is the viewport. That is, the percentage based sizes are based on the viewport's size. As discussed above, this can be somewhat ambiguous.
How do position:fixed elements get precentage-based sizes?
This is the one bit of good news in this story. However, resizing elements on the page at 60fps is a bad idea, so when are the sizes recalculated?
Viewport units allow the author to specify sizes in relation to the viewport size. e.g. If you want a box to be sized such that it fills half the viewport height you'd specify height: 50vh;
How does each browser treat the viewport units with regard to the URL bar?
The innerWidth and innerHeight properties of the window object specify the size of the browser window, not including any browser chrome (the UI concept, not the browser) like window borders and title bars. This does include scrollbars on desktop browsers.
How does each browser treat the URL bar with regard to innerHeight?
Clearly this is an interop disaster. I propose we make some small changes to Chrome to help the situation:
#1 means that, when the top controls are hidden, percentage-based heights will differ on elements based on whether they are position:fixed or not. position:fixed elements with percentage based heights will resize when the top controls are hidden or shown.
#2 doesn't apply to vh units set on a
position: fixedelement. They'll still resize in response to the URL bar.
It will be a little more work to make non-fixed elements fill the viewport after scrolling. I believe in most cases you'd want this for position:fixed elements anyway. In the cases that remain, we can get back to todays behavior by explicitly sizing the root element to window.innerHeight in the resize handler.
In general, the fixed-position viewport size can be read from window.innerHeight and the ICB size can be read from documentElement.clientHeight.
I've provided a test page that attempts to demonstrate all the points talked about here. Try Chrome 56 (currently Chrome Dev channel to see the changed behavior. The four bars on the right of the page are all possible combinations of 99%, 99vh, position:fixed and position:absolute provided on a scrollable page. Hiding the URL bar shows how it affects each. Resize events are printed down the page.