Unidades de viewport podem ser controversas, e parte disso é devido à maneira como os navegadores móveis tornaram as coisas mais complicadas ao terem suas próprias opiniões sobre como implementá-las.
Caso em questão: a barra de rolagem deve ser levada em consideração para a unidade vw
? E quanto à navegação de um site ou aos controles de página, eles devem ser considerados no cálculo? Depois, existem atributos físicos dos próprios dispositivos que não podem ser ignorados — olá, notch!
Recentemente, publicamos o artigo Algumas coisas que você deve saber ao trabalhar com unidades de viewport, em que abordávamos um pouco sobre isso, mas o que você vai aprender a seguir, realmente vai um passo além.
Um pouco de contexto
A especificação é bastante vaga sobre como as unidades de viewport devem ser calculadas. Em dispositivos móveis, geralmente nos preocupamos com a altura vertical; portanto, vejamos especificamente sobre viewport height (vh
):
unidade vh
Igual a 1% da altura do containing block inicial.
Então, sim, não há orientações claras quando se trata de lidar com diferenciações específicas de dispositivos e navegadores.
vh
foi inicialmente calculado pela viewport atual do seu navegador. Se você abriu o navegador e começou a carregar um site, 1vh
era igual a 1% da altura da tela, menos a interface do navegador.
Mas… Se você começar a rolar a tela, é uma história diferente! Depois que você passa por uma parte da interface do navegador, como a barra de endereço, o valor vh
será atualizado e o resultado será um salto estranho (awkward jump) no conteúdo.
O Safari para iOS foi um dos primeiros navegadores móveis a atualizar sua implementação, escolhendo definir um valor fixo para vh
com base na altura máxima da tela. Ao fazer isso, o usuário não experimentaria saltos na página depois que a barra de endereços ficasse fora de vista. O navegador móvel do Chrome seguiu o exemplo.
Embora o uso de um valor fixo seja bom, isso também significa que você não pode ter um elemento de altura total se a barra de endereço estiver em exibição. A parte inferior do elemento será cortada.
CSS Custom Properties: o truque para corrigir o dimensionamento
CSS Custom Properties (mais conhecidas como Variáveis CSS) e algumas linhas de JavaScript podem ser a maneira perfeita de obter o dimensionamento consistente e correto que eu precisava.
Em JavaScript, é possível obter o valor da viewport atual usando a variável global window.innerHeight
. Esse valor leva em conta a interface do navegador e é atualizado quando sua visibilidade muda. O truque é armazenar o valor da viewport em uma variável CSS e aplicá-lo ao elemento em vez da unidade vh
.
Digamos que, neste exemplo, a variável CSS seja --vh
. Isso significa que o código serial algo como:
1 2 3 4 |
.my-element { height: 100vh; /* Fallback para não-suporte a Custom Properties */ height: calc(var(--vh, 1vh) * 100); } |
Agora é pegar a altura interna da viewport no JavaScript:
1 2 3 4 5 |
// Altura da viewport multiplicada por 1% para obter um valor para vh let vh = window.innerHeight * 0.01; // Configura o valor em --vh na raiz do documento document.documentElement.style.setProperty('--vh', `${vh}px`); |
O JS vai pegar a altura da viewport e a ajustar em 1/100 desse total para que se tenha um valor para atribuir como o valor da unidade de altura da viewport. Então, pedimos educadamente ao JS para criar a variável CSS (--vh
) em :root
.
Como resultado, agora é possível usar usar --vh
como o valor de altura (tal como é possível com qualquer outra unidade de vh
), multiplicá-lo por 100 e conseguir a altura total desejada.
Opa, tem mais um detalhe!
Apesar de a solução já parecer concluída, aqueles com um olhar mais acurado para os detalhes podem ter percebido que o JavaScript é acionado, mas nunca atualiza o tamanho do elemento quando a altura da janela de visualização muda!
É possível atualizar o valor de --vh
para ficar escutando o evento resize
da janela. Isso é útil caso o usuário gire a tela do dispositivo ou a navegação saia da vista ao scrollar.
1 2 3 4 5 6 |
// Escuta o evento resize window.addEventListener('resize', () => { // Executa o mesmo script de antes let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); }); |
Além disso, convém implementar um método de debounce para o evento de redimensionamento para evitar o acionamento de muitos eventos enquanto o usuário está redimensionando a janela do navegador. Veja debouncing e throttling sendo explicados através de exemplos.
Agora sim o exemplo está funcionando corretamente. 👍
Conclusão
Só relembrando o aviso dado, esse macete para unidades de viewport em mobile provavelmente não cairá bem e todo e qualquer projeto. Você deve avaliar se o usará ou não.
Além disso, hoje em dia os navegadores tendem a serem atualizados muito rapidamente, portanto, esteja atendo e confira se a solução de hoje continuará relevante (e funcionando) amanhã.
E você, já escutou reclamações sobre esse problema e não sabia como arrumar na época? Bem, agora você sabe como fazer… De qualquer maneira, comente sobre esse caso aí nos comentários!