This issue is caused by the URL bars shrinking/sliding out of the way and changing the size of the #bg1 and #bg2 divs since they are 100% height and "fixed". Since the background image is set to "cover" it will adjust the image size/position as the containing area is larger.
Based on the responsive nature of the site, the background must scale. I entertain two possible solutions:
1) Set the #bg1, #bg2 height to 100vh. In theory, this an elegant solution. However, iOS has a vh bug (http://thatemil.com/blog/2013/06/13/viewport-relative-unit-strangeness-in-ios-6/). I attempted using a max-height to prevent the issue, but it remained.
2) The viewport size, when determined by Javascript, is not affected by the URL bar. Therefore, Javascript can be used to set a static height on the #bg1 and #bg2 based on the viewport size. This is not the best solution as it isn't pure CSS and there is a slight image jump on page load. However, it is the only viable solution I see considering iOS's "vh" bugs (which do not appear to be fixed in iOS 7).
var bg = $("#bg1, #bg2");
function resizeBackground() {
bg.height($(window).height());
}
$(window).resize(resizeBackground);
resizeBackground();
On a side note, I've seen so many issues with these resizing URL bars in iOS and Android. I understand the purpose, but they really need to think through the strange functionality and havoc they bring to websites. The latest change, is you can no longer "hide" the URL bar on page load on iOS or Chrome using scroll tricks.
EDIT: While the above script works perfectly for keeping the background from resizing, it causes a noticeable gap when users scroll down. This is because it is keeping the background sized to 100% of the screen height minus the URL bar. If we add 60px to the height, as swiss suggests, this problem goes away. It does mean we don't get to see the bottom 60px of the background image when the URL bar is present, but it prevents users from ever seeing a gap.
function resizeBackground() {
bg.height( $(window).height() + 60);
}
I found that Jason's answer wasn't quite working for me and I was still getting a jump. The Javascript ensured there was no gap at the top of the page but the background was still jumping whenever the address bar disappeared/reappeared. So as well as the Javascript fix, I applied transition: height 999999s
to the div. This creates a transition with a duration so long that it virtually freezes the element.
I've got a similar issue on a header of our website.
html, body {
height:100%;
}
.header {
height:100%;
}
This will end up in a jumpy scrolling experience on android chrome, because the .header-container will rescale after the url-bar hides and the finger is removed from screen.
CSS-Solution:
Adding the following two lines, will prevent that the url-bar hides and vertical scrolling is still possible:
html {
overflow: hidden;
}
body {
overflow-y: scroll;
-webkit-overflow-scrolling:touch;
}
The problem can be solved with a media query and some math. Here's a solution for a portait orientation:
@media (max-device-aspect-ratio: 3/4) {
height: calc(100vw * 1.333 - 9%);
}
@media (max-device-aspect-ratio: 2/3) {
height: calc(100vw * 1.5 - 9%);
}
@media (max-device-aspect-ratio: 10/16) {
height: calc(100vw * 1.6 - 9%);
}
@media (max-device-aspect-ratio: 9/16) {
height: calc(100vw * 1.778 - 9%);
}
Since vh will change when the url bar dissapears, you need to determine the height another way. Thankfully, the width of the viewport is constant and mobile devices only come in a few different aspect ratios; if you can determine the width and the aspect ratio, a little math will give you the viewport height exactly as vh should work. Here's the process
1) Create a series of media queries for aspect ratios you want to target.
use device-aspect-ratio instead of aspect-ratio because the latter will resize when the url bar dissapears
I added 'max' to the device-aspect-ratio to target any aspect ratios that happen to follow in between the most popular. THey won't be as precise, but they will be only for a minority of users and will still be pretty close to the proper vh.
remember the media query using horizontal/vertical , so for portait you'll need to flip the numbers
2) for each media query multiply whatever percentage of vertical height you want the element to be in vw by the reverse of the aspect ratio.
3) You have to determine the url bar height, and then minus that from the height. I haven't found exact measurements, but I use 9% for mobile devices in landscape and that seems to work fairly well.
This isn't a very elegant solution, but the other options aren't very good either, considering they are:
Having your website seem buggy to the user,
having improperly sized elements, or
Using javascript for some basic styling,
The drawback is some devices may have different url bar heights or aspect ratios than the most popular. However, using this method if only a small number of devices suffer the addition/subtraction of a few pixels, that seems much better to me than everyone having a website resize when swiping.
To make it easier, I also created a SASS mixin:
@mixin vh-fix {
@media (max-device-aspect-ratio: 3/4) {
height: calc(100vw * 1.333 - 9%);
}
@media (max-device-aspect-ratio: 2/3) {
height: calc(100vw * 1.5 - 9%);
}
@media (max-device-aspect-ratio: 10/16) {
height: calc(100vw * 1.6 - 9%);
}
@media (max-device-aspect-ratio: 9/16) {
height: calc(100vw * 1.778 - 9%);
}
}
All of the answers here are using window
height, which is affected by the URL bar. Has everyone forgotten about screen
height?
Here's my jQuery solution:
$(function(){
var $w = $(window),
$background = $('#background');
// Fix background image jump on mobile
if ((/Android|iPhone|iPad|iPod|BlackBerry/i).test(navigator.userAgent || navigator.vendor || window.opera)) {
$background.css({'top': 'auto', 'bottom': 0});
$w.resize(sizeBackground);
sizeBackground();
}
function sizeBackground() {
$background.height(screen.height);
}
});
Adding the .css()
part is changing the inevitably top-aligned absolute positioned element to bottom aligned, so there is no jump at all. Although, I suppose there's no reason not to just add that directly to the normal CSS.
We need the user agent sniffer because screen height on desktops would not be helpful.
Also, this is all assuming #background
is a fixed-position element filling the window.
For the JavaScript purists (warning--untested):
var background = document.getElementById('background');
// Fix background image jump on mobile
if ((/Android|iPhone|iPad|iPod|BlackBerry/i).test(navigator.userAgent || navigator.vendor || window.opera)) {
background.style.top = 'auto';
background.style.bottom = 0;
window.onresize = sizeBackground;
sizeBackground();
}
function sizeBackground() {
background.style.height = screen.height;
}
EDIT: Sorry that this does not directly answer your specific problem with more than one background. But this is one of the first results when searching for this problem of fixed backgrounds jumping on mobile.
I ran into this issue as well when I was trying to create an entrance screen that would cover the whole viewport. Unfortunately, the accepted answer no longer works.
1) Elements with the height set to 100vh
get resized every time the viewport size changes, including those cases when it is caused by (dis)appearing URL bar.
2) $(window).height()
returns values also affected by the size of the URL bar.
One solution is to "freeze" the element using transition: height 999999s
as suggested in the answer by AlexKempton. The disadvantage is that this effectively disables adaptation to all viewport size changes, including those caused by screen rotation.
So my solution is to manage viewport changes manually using JavaScript. That enables me to ignore the small changes that are likely to be caused by the URL bar and react only on the big ones.
function greedyJumbotron() {
var HEIGHT_CHANGE_TOLERANCE = 100; // Approximately URL bar height in Chrome on tablet
var jumbotron = $(this);
var viewportHeight = $(window).height();
$(window).resize(function () {
if (Math.abs(viewportHeight - $(window).height()) > HEIGHT_CHANGE_TOLERANCE) {
viewportHeight = $(window).height();
update();
}
});
function update() {
jumbotron.css('height', viewportHeight + 'px');
}
update();
}
$('.greedy-jumbotron').each(greedyJumbotron);
EDIT: I actually use this technique together with height: 100vh
. The page is rendered properly from the very beginning and then the javascript kicks in and starts managing the height manually. This way there is no flickering at all while the page is loading (or even afterwards).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With