CSS3 100vh não constante no navegador móvel

285

Eu tenho um problema muito estranho ... em todos os navegadores e versões móveis, encontrei esse comportamento:

  • todos os navegadores têm um menu superior quando você carrega a página (mostrando a barra de endereço, por exemplo), que desliza para cima quando você começa a rolar a página.
  • Às vezes, 100vh é calculado apenas na parte visível de uma janela de visualização; portanto, quando a barra do navegador desliza para cima, 100vh aumenta (em termos de pixels)
  • todo o layout repintar e reajustar, pois as dimensões foram alteradas
  • um efeito ruim para a experiência do usuário

Como evitar esse problema? Quando ouvi pela primeira vez sobre o viewport-height, fiquei empolgado e pensei em usá-lo para blocos de altura fixa em vez de usar javascript, mas agora acho que a única maneira de fazer isso é de fato o javascript com algum evento de redimensionamento ...

você pode ver o problema em: site de amostra

Alguém pode me ajudar com / sugerir uma solução CSS?


código de teste simples:

Nereo Costacurta
fonte
1
Se eu entendi bem a pergunta, o problema que você está enfrentando é no navegador móvel, a altura é mais do que a janela de visualização visível, alta ... certo?
Gaurav Aggarwal
Interessante, nunca notei isso antes. É principalmente a imagem de fundo que é visivelmente nervosa. Que tal você adicionar uma transition: 0.5sou mais, para tornar a alteração menos abrupta?
C14L
@GauravAggarwal não, exatly o oposto: a altura verdadeira porta de visualização é maior do que o fornecido pelo navegador quando a sua barra de endereços é visível ...
Nereo Costacurta
1
Como minha pergunta está se tornando popular, gostaria de dar meus 5 centavos: não seria mais inteligente manter a altura real da janela e apenas deslizar a barra de menus? não parece tão difícil. De fato, deve ser mais fácil ... dedo para cima -> barra de menus deslizar para cima até ficar invisível, dedo para baixo -> barra de menus para baixo até ficar completamente visível ... tudo junto com o corpo sem nenhum efeito de reajuste e saltos ...
Nereo Costacurta
4
O Google tem boas informações sobre isso: developers.google.com/web/updates/2016/12/url-bar-resizing Você pode usar 100% em vez de 100vh SE tiver alterado a altura do corpo para 100%
Benisburgers

Respostas:

203

Infelizmente isso é intencional…

Esse é um problema bem conhecido (pelo menos no safari mobile), que é intencional, pois evita outros problemas. Benjamin Poulain respondeu a um bug do webkit :

Isso é completamente intencional. Demorou um pouco de trabalho de nossa parte para alcançar esse efeito. :)

O problema básico é este: a área visível muda dinamicamente à medida que você rola. Se atualizarmos a altura da janela de exibição CSS de acordo, precisamos atualizar o layout durante a rolagem. Não apenas isso parece uma merda, mas fazer isso a 60 FPS é praticamente impossível na maioria das páginas (60 FPS é a taxa de quadros da linha de base no iOS).

É difícil mostrar a parte "parece uma merda", mas imagine que enquanto você rola, o conteúdo se move e o que você deseja na tela muda continuamente.

A atualização dinâmica da altura não estava funcionando, tivemos algumas opções: soltar unidades de viewport no iOS, corresponder ao tamanho do documento como antes do iOS 8, usar o tamanho pequeno da visualização, usar o tamanho grande da visualização.

A partir dos dados que tínhamos, usar o tamanho de visualização maior foi o melhor compromisso. A maioria dos sites que usam unidades de visualização estava ótima na maioria das vezes.

Nicolas Hoizey pesquisou bastante isso: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile -browsers.html

Nenhuma correção planejada

Nesse ponto, não há muito o que fazer, exceto evitar o uso da altura da janela de exibição em dispositivos móveis. O Chrome também mudou para isso em 2016:

nada
fonte
7
Sim, como eu penso. A única solução viável é uma solução com script. Pelo que sei, $(window).height()não é afetado por esse bug, então vou por esse caminho. obrigado!
Nereo Costacurta
3
Script era o único caminho para mim e funcionava perfeitamente.
precisa saber é o seguinte
456
Resposta mais deprimente de todos os tempos.
Winnemucca
15
Desde a versão 56 do Chrome, o vh é sempre calculado como se a barra de URL estivesse oculta e, portanto, o vh não aumenta na rolagem, exceto position:fixed. Isso é semelhante à implementação no Safari. Leia mais: developers.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia
4
O Chrome mais recente não muda vhou <html> 100%altura após o carregamento inicial. Na verdade, isso é ótimo - como o layout debulhando / refluindo quando as ocultas da barra de URL podem parecer terríveis. Firefoxe Edge Mobileainda atualizar dinamicamente vhe <html>altura, causando muita lacrimejamento e redimensionamento de todos os componentes durante a rolagem, especialmente ruim se estiver usando SVGs de imagens de fundo
Drenai
142

Você pode tentar min-height: -webkit-fill-available;em seu css em vez de 100vh. Deve ser resolvido

Saurabh Jain
fonte
20
Gostaria de sair min-height: 100vh;como um substituto onde -webkit-fill-availablenão é suportado.
Tammy Tee
Uau, obrigada !! Com isso, não preciso mais de "react-div-100vh"!
Andres Elizondo
1
a resposta mais útil!
Gauthier
O @TammyTee está certo, é melhor manter min-height: 100vh;-se lá, pois, caso contrário , ele trava em navegadores como o Firefox (pelo menos na área de trabalho, o iOS usa o webkit, independentemente do navegador).
JoniVR 01/10/19
2
Essa foi uma solução muito boa, mas parece ter mudado no iOS 13 - estou vendo espaço extra na parte inferior da primeira seção, mas funciona exatamente como desejado no Safari para iOS 12.x codepen.io/RwwL/pen / PowjvPq (você teria que bifurcar isso e visualizá-lo no modo de depuração do CodePen no iOS para realmente entender o que quero dizer).
RwwL
27

no meu aplicativo, eu faço da seguinte maneira (postscript datilografado e aninhado, altere o código adequadamente):

const appHeight = () => {
    const doc = document.documentElement
    doc.style.setProperty('--app-height', `${window.innerHeight}px`)
}
window.addEventListener('resize', appHeight)
appHeight()

no seu css:

:root {
   --app-height: 100%;
}

html,
body {
    padding: 0;
    margin: 0;
    overflow: hidden;
    width: 100vw;
    height: 100vh;

    @media not all and (hover:hover) {
        height: var(--app-height);
    }
}

funciona pelo menos no chrome mobile e ipad. O que não funciona é quando você adiciona seu aplicativo à tela inicial no iOS e altera a orientação algumas vezes - de alguma forma, os níveis de zoom interferem com o valor innerHeight, posso postar uma atualização se encontrar uma solução para ele.

Demo

Andreas Herd
fonte
2
Esta é realmente uma abordagem fantástica para um problema muito irritante.
Michael Giovanni Pumo 14/03/19
1
Gostaria de adicionar um aviso de que "@media nem todos e (hover: hover) {" não é uma maneira à prova de balas para detectar navegadores móveis. Sinta-se livre para usar outra coisa.
Andreas Herd
Eu gosto muito dessa abordagem. Um comentário que eu faria é sobre o uso do ouvinte de eventos. Isso causa uma nova pintura da página e eu a uso apenas em alguns cenários quando é absolutamente crítico para o usuário ver a viewport correta (ou seja, a visualização inicial da página inicial)
Zach Gollwitzer em
22

Para muitos dos sites que eu construo, o cliente solicita um banner de 100vh e, exatamente como você encontrou, resulta em uma experiência "irregular" no celular quando você começa a rolar. É assim que resolvo o problema para uma experiência consistente e uniforme em todos os dispositivos:

Primeiro, defino meu elemento CSS do banner como height:100vh

Então eu uso o jQuery para obter a altura em pixels do meu elemento banner e aplicar um estilo embutido usando essa altura.

var viewportHeight = $('.banner').outerHeight();
$('.banner').css({ height: viewportHeight });

Isso resolve o problema em dispositivos móveis, como quando a página é carregada, o elemento banner é definido como 100vh usando CSS e o jQuery substitui isso colocando CSS embutido no elemento banner, o que impede o redimensionamento quando um usuário começa a rolar.

No entanto, na área de trabalho, se um usuário redimensionar sua janela do navegador, meu elemento banner não será redimensionado porque agora possui uma altura fixa definida em pixels devido ao jQuery acima. Para resolver isso, uso o Mobile Detect para adicionar uma classe 'mobile' ao corpo do meu documento. E então envolvo o jQuery acima em uma instrução if:

if ($('body').hasClass('mobile')) {
  var viewportHeight = $('.banner').outerHeight();
  $('.banner').css({ height: viewportHeight });
}

Como resultado, se um usuário estiver em um dispositivo móvel, a classe 'mobile' estará presente no corpo da minha página e o jQuery acima será executado. Portanto, meu elemento banner só obterá o CSS embutido aplicado em dispositivos móveis enquanto isso, no desktop, a regra original de 100vh CSS permanece em vigor.


fonte
4
Eu o ajustaria para usar $('.banner').css({ height: window.innerHeight });, que é a altura "real" da janela de exibição. Exemplo de como o innerHeight funciona
Martin
8
O que é document.querySelector('.banner').style.height = window.innerHeight;para pessoas que escrevem JavaScript real.
Fabian von Ellerts 12/03/19
18

Veja esta resposta: https://css-tricks.com/the-trick-to-viewport-units-on-mobile/

// 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`);

// We listen to the resize event
window.addEventListener('resize', () => {
  // We execute the same script as before
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
});
body {
  background-color: #333;
}

.module {
  height: 100vh; /* Use vh as a fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
  margin: 0 auto;
  max-width: 30%;
}

.module__item {
  align-items: center;
  display: flex;
  height: 20%;
  justify-content: center;
}

.module__item:nth-child(odd) {
  background-color: #fff;
  color: #F73859;
}

.module__item:nth-child(even) {
  background-color: #F73859;
  color: #F1D08A;
}
<div class="module">
  <div class="module__item">20%</div>
  <div class="module__item">40%</div>
  <div class="module__item">60%</div>
  <div class="module__item">80%</div>
  <div class="module__item">100%</div>
</div>

manuel-84
fonte
2
solução inteligente, eu estava enfrentando problema com altura viewport em dispositivos móveis, isso deve ser aceito como solução também (especialmente para dispositivos móveis)
Julio Vedovatto
solução muito boa. No meu caso, eu não usei o ponyfill, mas lógico que, para todos os sites de desktop, eu uso 100vh, enquanto que para dispositivos móveis eu uso var (não temos o IE11 no mundo móvel).
FrEaKmAn
Única solução que funcionou para elementos aninhados. Em qualquer outra pessoa que tenha que substituir algumas centenas de instâncias, é possível (pelo menos na maioria das vezes) corresponder ao regexp (-)? ([\ D.] +) Vh e substituí-lo por calc (var (- vh) * R $ 1,00 (2)
ecc521 04/04
10

Eu vim com um componente React - verifique se você usa o React ou procure o código-fonte se não o fizer, para que você possa adaptá-lo ao seu ambiente.

Ele define a altura do div em tela cheia window.innerHeighte atualiza-o no redimensionamento da janela.

Mikhail Vasin
fonte
3
Nem todos os heróis usam bonés. Você está bem, por exemplo. Muito obrigado. economizou horas de tempo.
Narayan
E para os amantes do Vue ... github.com/razumnyak/vue-div-100vh
Tony O'Hagan
6

Como eu estava procurando uma solução em alguns dias, aqui está a minha para todos que usam o VueJS com Vuetify (minha solução usa v-app-bar, v-navigation-drawer e v-footer): Criei App.scss (usado no App. vue) com o seguinte conteúdo:

.v-application {
    height: 100vh;
    height: -webkit-fill-available;
}

.v-application--wrap {
    min-height: 100vh !important;
    min-height: -webkit-fill-available !important;
}

Jürgen P.
fonte
1
Isso funciona bem e pode ser generalizado fora do mundo Vue / Vuetify.
leo
5

Para mim, esse truque fez um trabalho:

height: calc(100vh - calc(100vh - 100%))
Patryk Janik
fonte
É diferente da altura: 100%?
FF_Dev
Sim, 100vh é 100% da altura da janela de visualização (seu dispositivo), quando 100% puro preenche apenas toda a área pai disponível (o bloco pai pode ter, por exemplo, 50px de altura e será preenchido apenas para essa altura pai). Em grande curta.
Patryk Janik 04/06
4

@nils explicou claramente.

O que vem a seguir então?

Acabei de voltar a usar relativo 'clássico' %(porcentagem) em CSS.

Muitas vezes, é mais difícil implementar algo do que seria usado vh, mas pelo menos, você tem uma solução bastante estável que funciona em diferentes dispositivos e navegadores sem falhas estranhas na interface do usuário.

klimat
fonte
1
A altura: 100% para banners ainda salta em navegadores de celular. Tenho testado no Android e, até agora, o Chrome e o Opera Mini oferecem 100% da altura visível do vice-presidente, e não mudam na rolagem. Edge e Firefox alteram a altura de 100% e repintam a exibição. Firefox é especialmente ruim, com lacrimejamento, texto rerendered, muito obviamente re-disposição
Drenai
1
@Drenai, você tem certeza de que implementou isso corretamente? Você pode provar isso em um pequeno exemplo no jsfiddle.net ou algo assim?
klimat
Sim, tenho certeza :-)
Drenai 28/11
3

Acabei de encontrar um aplicativo Web que eu projetei com esse problema nos iPhones e iPads e encontrei um artigo sugerindo resolvê-lo usando consultas de mídia direcionadas a dispositivos Apple específicos.

Não sei se posso compartilhar o código desse artigo aqui, mas o endereço é este: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Citando o artigo: "apenas combine a altura do elemento com a altura do dispositivo usando consultas de mídia direcionadas às versões mais antigas da resolução do iPhone e iPad".

Eles adicionaram apenas 6 consultas de mídia para adaptar elementos de altura total e devem funcionar como totalmente implementados em CSS.

Editar pendente: não posso testá-lo no momento, mas voltarei e relatarei meus resultados.

Jahaziel
fonte
23
ainda à espera de esses resultados 😉
gman
2
Me desculpe, eu não voltei como prometido. Mas, depois de mexer com o HTML e o CSS por muito mais tempo do que eu gostaria, mudei o design do meu layout e encontrei uma maneira que funcionava "ok" para minhas necessidades, mas não era uma solução universal.
Jahaziel
2

Como sou novo, não posso comentar sobre outras respostas.

Se alguém está procurando uma resposta para fazer isso funcionar (e pode usar javascript - como parece ser necessário para fazer isso funcionar no momento), essa abordagem funcionou muito bem para mim e é responsável também pela alteração da orientação móvel. Eu uso o Jquery para o código de exemplo, mas deve ser possível com o vanillaJS.

-Primeiro, eu uso um script para detectar se o dispositivo está tocando ou passa o mouse. Exemplo básico:

if ("ontouchstart" in document.documentElement) {
    document.body.classList.add('touch-device');

} else {
    document.body.classList.add('hover-device');
}

Isso adiciona classe ao elemento body de acordo com o tipo de dispositivo (foco ou toque) que pode ser usado posteriormente para o script de altura.

-Em seguida, use este código para definir a altura do dispositivo na carga e na mudança de orientação:

if (jQuery('body').hasClass("touch-device")) {
//Loading height on touch-device
    function calcFullHeight() {
        jQuery('.hero-section').css("height", $(window).height());
    }

    (function($) {
        calcFullHeight();

        jQuery(window).on('orientationchange', function() {
            // 500ms timeout for getting the correct height after orientation change
            setTimeout(function() {
                calcFullHeight();
            }, 500);

        });
    })(jQuery);

} else {
    jQuery('.hero-section').css("height", "100vh");


}

-Tempo limite é definido para que o dispositivo calcule a nova altura corretamente na mudança de orientação. Se não houver tempo limite, na minha experiência, a altura não estará correta. 500ms pode ser um exagero, mas funcionou para mim.

-100vh em dispositivos suspensos é um substituto se o navegador substituir o CSS 100vh.

tjgoodman
fonte
2
Touchstart não é compatível com todos os navegadores, e alguns dispositivos não móveis tê-lo developer.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai
O @Drenai Touchstart é suportado na maioria dos navegadores móveis. Na área de trabalho, o suporte é um pouco menor, o IE, o Opera e o Safari não o suportam. No entanto, a pergunta original é voltada para dispositivos móveis e não para computadores. O que torna isso uma resposta válida.
Paul
Obrigado pelos comentários! Eu usei principalmente essas táticas para superar o reajuste e o salto da tela em alguns navegadores móveis. Se o navegador não suportar o touchstart, o fallback será o css 100vh - se 100vh não for suportado, esse será um tópico diferente. Se o dispositivo de mesa tiver suporte de toque, a altura será calculada com javascript. Em seguida, perdemos o recálculo no redimensionamento fornecido por 100vh, mas não é um problema fatal em geral, pois não há alterações de orientação na área de trabalho.
tjgoodman
Além disso, o cálculo da altura também pode ser anexado ao evento de redimensionamento, mas podemos enfrentar o mesmo problema exato de reajuste e saltos em dispositivos móveis. Portanto, eu achei essas táticas práticas.
tjgoodman
1
@Paul Se os navegadores de desktop Chrome e Firefox tiverem touchstart, certamente esse é o recurso errado para detectar um navegador móvel? Eu só testei no Chrome e Firefox no Windows, e ambos passar este teste
Drenai
2

O código a seguir resolveu o problema (com jQuery).

var vhHeight = $("body").height();
var chromeNavbarHeight = vhHeight - window.innerHeight;
$('body').css({ height: window.innerHeight, marginTop: chromeNavbarHeight });

E os outros elementos usam %como uma unidade para substituir vh.

Ray1422
fonte
2

Aqui está uma solução que eu usei para o meu aplicativo React.

iPhone 11 Pro e iPhone Pro Max - 120px

iPhone 8 - 80px

max-height: calc(100vh - 120px);

É uma solução comprometida, mas relativamente simples

Trevor Wood
fonte
1

O seguinte funcionou para mim:

html { height: 100vh; }

body {
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100vw;
}

/* this is the container you want to take the visible viewport  */
/* make sure this is top-level in body */
#your-app-container {
  height: 100%;
}

O bodylevará a altura janela visível e #your-app-containercom height: 100%vai fazer esse recipiente tomar a altura janela visível.

Rico Kahler
fonte
0

Porque ele não será corrigido, você pode fazer algo como:

# html
<body>
  <div class="content">
    <!-- Your stuff here -->
  </div>
</body>

# css
.content {
  height: 80vh;
}

Para mim, era a solução mais rápida e pura do que brincar com o JavaScript, que não funcionava em muitos dispositivos e navegadores.

Basta usar o valor adequado vhque atenda às suas necessidades.

turkus
fonte
0

O uso de vh em dispositivos móveis não funcionará com 100vh, devido às opções de design que utilizam toda a altura do dispositivo, sem incluir barras de endereço etc.

Se você está procurando um layout que inclua alturas de div proporcionais à verdadeira altura da vista, eu uso a seguinte solução css pura:

:root {
  --devHeight: 86vh; //*This value changes
}

.div{
    height: calc(var(--devHeight)*0.10); //change multiplier to suit required height
}

Você tem duas opções para definir a altura da janela de visualização, defina manualmente --devHeight como uma altura que funcione (mas será necessário inserir esse valor para cada tipo de dispositivo que você está codificando)

ou

Use javascript para obter a altura da janela e, em seguida, atualize --devheight ao carregar e atualizar a viewport (no entanto, isso requer o uso de javascript e não é uma solução css pura)

Depois de obter a altura correta da vista, você pode criar várias divs em uma porcentagem exata da altura total da janela de visualização, simplesmente alterando o multiplicador em cada div à qual você atribui a altura.

0,10 = 10% da altura da vista 0,57 = 57% da altura da vista

Espero que isso possa ajudar alguém;)

Ray Andison
fonte
1
Essa é uma boa solução. Você pode ler mais sobre isso neste artigo em truques de CSS .
Amaury Hanser
0

Você pode fazer isso adicionando o seguinte script e estilo

  function appHeight() {
    const doc = document.documentElement
    doc.style.setProperty('--vh', (window.innerHeight*.01) + 'px');
  }
  window.addEventListener('resize', appHeight);
  appHeight();

Estilo

.module {
 height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}
Sarath Ak
fonte
0

Tente html, body { height: 100% }algo com o efeito de 100vh em dispositivos móveis.

Sam Bokai
fonte
-1

Você pode tentar atribuir position: fixed; top: 0; bottom: 0;propriedades ao seu contêiner.

Hasan upinion
fonte
2
Adicione mais informações sobre por que essa é uma resposta à pergunta.
Infectado Drake