CSS3 100vh no constante en el navegador móvil

285

Tengo un problema muy extraño ... en cada navegador y versión móvil encontré este comportamiento:

  • todos los navegadores tienen un menú superior cuando carga la página (que muestra la barra de direcciones, por ejemplo) que se desliza hacia arriba cuando comienza a desplazarse por la página.
  • 100vh a veces se calcula solo en la parte visible de una ventana gráfica, por lo que cuando la barra del navegador se desliza hacia arriba 100vh aumenta (en términos de píxeles)
  • Todos los diseños se vuelven a pintar y reajustar ya que las dimensiones han cambiado
  • un mal efecto nervioso para la experiencia del usuario

¿Cómo puede evitar este problema? Cuando escuché por primera vez sobre viewport-height estaba emocionado y pensé que podría usarlo para bloques de altura fija en lugar de usar javascript, pero ahora creo que la única forma de hacerlo es, de hecho, javascript con algún evento de cambio de tamaño ...

puede ver el problema en: sitio de muestra

¿Alguien puede ayudarme / sugerir una solución CSS?


código de prueba simple:

Nereo Costacurta
fuente
1
si entendí bien la pregunta, el problema al que se enfrenta es en el navegador móvil, la altura es más que la altura de la vista visible ... ¿verdad?
Gaurav Aggarwal
Interesante, nunca lo noté antes. Principalmente, la imagen de fondo es notablemente nerviosa. ¿Qué tal si agrega un más transition: 0.5so menos, para que el cambio sea menos abrupto?
C14L
@GauravAggarwal no, exactamente lo contrario: la altura real de la ventana gráfica es mayor que la proporcionada por el navegador cuando su barra de direcciones es visible ...
Nereo Costacurta
1
Como mi pregunta se está volviendo popular, me gustaría dar mis 5 centavos: ¿no sería más inteligente mantener la altura real de la ventana y solo deslizar hacia arriba la barra de menú? No parece tan difícil. De hecho, debería ser más fácil ... dedo arriba -> barra de menú deslizar hacia arriba hasta que sea invisible, dedo abajo -> barra de menú deslizar hacia abajo hasta que esté completamente visible ... todo junto con el cuerpo sin ningún efecto de reajuste y salto ...
Nereo Costacurta
44
Google tiene buena información sobre esto: developers.google.com/web/updates/2016/12/url-bar-resizing Puede usar 100% en lugar de 100vh SI ha cambiado la altura del cuerpo al 100%
Benisburgers

Respuestas:

203

Lamentablemente esto es intencional ...

Este es un problema bien conocido (al menos en safari mobile), que es intencional, ya que evita otros problemas. Benjamin Poulain respondió a un error de webkit :

Esto es completamente intencional. Tomó bastante trabajo de nuestra parte lograr este efecto. :)

El problema base es este: el área visible cambia dinámicamente a medida que se desplaza. Si actualizamos la altura de la vista CSS en consecuencia, debemos actualizar el diseño durante el desplazamiento. No solo eso parece una mierda, sino que hacerlo a 60 FPS es prácticamente imposible en la mayoría de las páginas (60 FPS es la velocidad de fotogramas de referencia en iOS).

Es difícil mostrarle la parte de "parece una mierda", pero imagínese mientras se desplaza, el contenido se mueve y lo que desea en la pantalla cambia continuamente.

La actualización dinámica de la altura no funcionaba, teníamos algunas opciones: soltar las unidades de ventana en iOS, hacer coincidir el tamaño del documento como antes de iOS 8, usar el tamaño de vista pequeña, usar el tamaño de vista grande.

A partir de los datos que teníamos, usar el tamaño de vista más grande fue el mejor compromiso. La mayoría de los sitios web que utilizan unidades de ventana gráfica se veían geniales la mayor parte del tiempo.

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

Sin arreglo planeado

En este punto, no hay mucho que pueda hacer, excepto abstenerse de usar la altura de la ventana gráfica en dispositivos móviles. Chrome también cambió a esto en 2016:

nils
fuente
77
Sí, como creo. La única solución viable es una solución programada. Por lo que sé, $(window).height()este error no se ve afectado, así que iré por ese camino. ¡gracias!
Nereo Costacurta
3
El guión era la única forma para mí y funcionaba perfectamente.
captDaylight
456
La respuesta más deprimente de todos.
Winnemucca
15
Desde Chrome versión 56, la vh siempre se calcula como si la barra de URL estuviera oculta y, por lo tanto, vh no aumenta en el desplazamiento, a excepción de position:fixed. Esto es similar a la implementación en Safari. Leer más: developers.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia
44
El último Chrome no cambia vho la <html> 100%altura después de la carga inicial. Esto es realmente genial, ya que la distribución / reflujo del diseño cuando las ocultaciones de la barra de URL pueden parecer terribles. Firefoxy Edge Mobileaún se actualiza vhy se <html>eleva dinámicamente , lo que provoca muchos rasgaduras y cambios de tamaño de todos los componentes al desplazarse, especialmente malo si se usan SVG para imágenes de fondo
Drenai
142

Puedes probar min-height: -webkit-fill-available;en tu CSS en lugar de 100vh. Debe ser resuelto

Saurabh Jain
fuente
20
Me iría min-height: 100vh;como una alternativa donde -webkit-fill-availableno es compatible.
Tammy Tee
¡¡Wow gracias!! ¡Con esto ya no necesito "react-div-100vh"!
Andres Elizondo
1
La respuesta más útil!
Gauthier
@TammyTee tiene razón, es mejor mantenerse min-height: 100vh;allí, ya que de lo contrario se rompería en navegadores como Firefox (al menos en el escritorio, iOS usa webkit sin importar qué navegador).
JoniVR
2
Esta fue una solución muy buena, pero parece haber cambiado en iOS 13: veo espacio adicional en la parte inferior de la primera sección, pero funciona exactamente como se desea en Safari para iOS 12.x codepen.io/RwwL/pen / PowjvPq (tendrías que bifurcar esto y luego verlo en modo de depuración CodePen en iOS para ver realmente a qué me refiero).
RwwL
27

en mi aplicación lo hago así (mecanografiado y postcss anidado, así que cambie el código en consecuencia):

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

en tu 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 al menos en Chrome Mobile y iPad. Lo que no funciona es cuando agrega su aplicación a la pantalla de inicio en iOS y cambia la orientación varias veces; de alguna manera, los niveles de zoom se meten con el valor de la altura interna, podría publicar una actualización si encuentro una solución.

Manifestación

Andreas Herd
fuente
2
Este es realmente un enfoque fantástico para un problema muy molesto.
Michael Giovanni Pumo
1
Me gustaría agregar una advertencia de que "@media no todos y (hover: hover) {" no es una forma a prueba de balas para detectar navegadores móviles. Siéntase libre de usar otra cosa.
Andreas Herd
Me gusta mucho este enfoque. Un comentario que haría es sobre el uso del detector de eventos. Esto provoca una nueva pintura de la página y solo la uso en ciertos escenarios cuando es absolutamente crítico para el usuario ver la ventana gráfica correcta (es decir, la vista inicial de la página de inicio)
Zach Gollwitzer
22

Para muchos de los sitios que construyo, el cliente solicitará un banner de 100vh y, tal como lo ha encontrado, resulta en una mala experiencia "nerviosa" en el móvil cuando comienza a desplazarse. Así es como resuelvo el problema para una experiencia uniforme y uniforme en todos los dispositivos:

Primero configuré mi elemento de banner CSS en height:100vh

Luego uso jQuery para obtener la altura en píxeles de mi elemento de banner y aplicar un estilo en línea usando esta altura.

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

Hacer esto resuelve el problema en los dispositivos móviles, ya que cuando se carga la página, el elemento de banner se establece en 100vh usando CSS y luego jQuery anula esto al colocar CSS en línea en mi elemento de banner que evita que se redimensione cuando un usuario comienza a desplazarse.

Sin embargo, en el escritorio si un usuario cambia el tamaño de la ventana de su navegador, mi elemento de banner no cambiará de tamaño porque ahora tiene una altura fija establecida en píxeles debido a la jQuery anterior. Para abordar esto, uso Mobile Detect para agregar una clase 'móvil' al cuerpo de mi documento. Y luego envuelvo el jQuery anterior en una declaración if:

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

Como resultado, si un usuario está en un dispositivo móvil, la clase 'móvil' está presente en el cuerpo de mi página y se ejecuta jQuery anterior. Por lo tanto, mi elemento de banner solo obtendrá el CSS en línea aplicado en dispositivos móviles, mientras que en el escritorio la regla CSS original de 100vh permanece en su lugar.


fuente
44
Lo ajustaría para usarlo $('.banner').css({ height: window.innerHeight });, que es la altura "real" de la ventana gráfica. Ejemplo de cómo funciona innerHeight
Martin
8
Lo cual es document.querySelector('.banner').style.height = window.innerHeight;para personas que escriben JavaScript real.
Fabian von Ellerts
18

Mira esta respuesta: 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
fuente
2
solución inteligente, estaba enfrentando un problema con la altura de la ventana gráfica en dispositivos móviles, esto también debería aceptarse como solución (especialmente para dispositivos móviles)
Julio Vedovatto
Muy buena solución. Solo en mi caso, no utilicé ponyfill, sino más bien la lógica de que para todos los sitios de escritorio utilizo 100vh, mientras que para dispositivos móviles utilizo var (no tenemos IE11 en el mundo móvil).
FrEaKmAn
Única solución que funcionó para elementos anidados. En cualquier otra persona que tenga que reemplazar un par de cientos de instancias, puede (al menos la mayor parte del tiempo) hacer coincidir la expresión regular (-)? ([\ D.] +) Vh y reemplazarla con calc (var (- vh) * $ 1 $ 2)
ecc521
10

Se me ocurrió un componente React. Compruébalo si usas React o exploras el código fuente si no lo haces, para que puedas adaptarlo a tu entorno.

Establece la altura del div en pantalla completa window.innerHeighty luego la actualiza en los cambios de tamaño de la ventana.

Mikhail Vasin
fuente
3
No todos los héroes usan gorras. Estás bien, por ejemplo. Muchas gracias. ahorró horas de tiempo.
NarayaN
Y para los amantes de Vue ... github.com/razumnyak/vue-div-100vh
Tony O'Hagan
6

Como estaba buscando una solución algunos días, aquí está el mío para todos los que usan VueJS con Vuetify (mi solución usa v-app-bar, v-navigation-drawer y v-footer): creé App.scss (usado en la aplicación. vue) con el siguiente contenido:

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

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

Jürgen P.
fuente
1
Esto funciona bien y puede generalizarse fuera del mundo Vue / Vuetify.
Leo
5

Para mí, tal truco hizo un trabajo:

height: calc(100vh - calc(100vh - 100%))
Patryk Janik
fuente
¿Es diferente a la altura: 100%?
FF_Dev
Sí, 100vh es el 100% de la altura de la ventana gráfica (su dispositivo), cuando el 100% puro solo llena toda el área principal disponible (el bloque principal puede tener, por ejemplo, una altura de 50 px y se llenará solo a esta altura principal). En gran corto.
Patryk Janik
4

@nils lo explicó claramente.

¿Qué sigue entonces?

Acabo de volver a usar el 'clásico' relativo %(porcentaje) en CSS.

A menudo es más esfuerzo implementar algo de lo que estaría usando vh, pero al menos, tiene una solución bastante estable que funciona en diferentes dispositivos y navegadores sin fallas extrañas en la interfaz de usuario.

klimat
fuente
1
La altura: 100% para las pancartas aún salta en los navegadores móviles. Lo he estado probando en Android, y hasta ahora Chrome y Opera Mini dan el 100% como la altura de VP visible, y no cambian en el desplazamiento. Edge y Firefox cambian la altura al 100% y repintan la vista. Firefox es especialmente malo, con rasgaduras, texto reenviado, muy obviamente rediseño
Drenai
1
@Drenai, ¿estás seguro de que implementas esto correctamente? ¿Puedes probar eso en un pequeño ejemplo en jsfiddle.net?
klimat
Sí, estoy seguro :-)
Drenai
3

Acabo de encontrar una aplicación web que diseñé que tiene este problema con iPhones y iPads, y encontré un artículo que sugiere resolverlo utilizando consultas de medios dirigidas a dispositivos Apple específicos.

No sé si puedo compartir el código de ese artículo aquí, pero la dirección es esta: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Citando el artículo: "solo haga coincidir la altura del elemento con la altura del dispositivo utilizando consultas de medios que se dirigen a las versiones anteriores de la resolución de iPhone y iPad".

Agregaron solo 6 consultas de medios para adaptar elementos de altura completa, y debería funcionar ya que está completamente implementado con CSS.

Editar pendiente: no puedo probarlo en este momento, pero volveré e informaré mis resultados.

Jahaziel
fuente
23
Todavía estoy esperando esos resultados 😉
gman
2
Lo siento, no regresé como lo prometí. Pero después de jugar con el HTML y el CSS durante mucho más tiempo de lo que me hubiera gustado, cambié el diseño de mi diseño y encontré una forma que funcionaba "bien" para mis necesidades, pero no era una solución universal.
Jahaziel
2

Como soy nuevo, no puedo comentar sobre otras respuestas.

Si alguien está buscando una respuesta para que esto funcione (y puede usar JavaScript, ya que parece ser necesario para que esto funcione en este momento), este enfoque me ha funcionado bastante bien y también explica el cambio de orientación móvil. Yo uso Jquery para el código de ejemplo, pero debería ser factible con vanillaJS.

-Primero, uso una secuencia de comandos para detectar si el dispositivo es táctil o flotante. Ejemplo de huesos desnudos:

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

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

Esto agrega clase al elemento del cuerpo de acuerdo con el tipo de dispositivo (desplazamiento o toque) que se puede usar más adelante para el script de altura.

-A continuación, use este código para establecer la altura del dispositivo en la carga y en el cambio de orientación:

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");


}

-El tiempo de espera está configurado para que el dispositivo calcule la nueva altura correctamente en el cambio de orientación. Si no hay tiempo de espera, en mi experiencia, la altura no será correcta. 500 ms puede ser una exageración, pero ha funcionado para mí.

-100vh en dispositivos de desplazamiento es una alternativa si el navegador anula el CSS 100vh.

tjgoodman
fuente
2
Touchstart no es compatible con todos los navegadores, y algunos dispositivos no móviles lo tienen developer.mozilla.org/en-US/docs/Web/Events/touchstart
Drenai
@Drenai Touchstart es compatible con la mayoría de los navegadores móviles. En el escritorio, el soporte es un poco menor, IE, Opera y Safari no lo admiten. Sin embargo, la pregunta original está dirigida a dispositivos móviles y no a computadoras de escritorio. Lo que hace que esta sea una respuesta válida.
Paul
! Gracias por los comentarios! He utilizado principalmente estas tácticas para superar el reajuste y la agitación de la pantalla en algunos navegadores móviles. Si el navegador no admite el inicio táctil, el respaldo será el css 100vh; si no se admite 100vh, ese es un tema diferente. Si el dispositivo de escritorio tiene soporte táctil, la altura se calculará con javascript. Luego perdemos el recálculo en el cambio de tamaño que proporciona 100vh, pero no es un problema fatal en general, ya que no hay cambios de orientación en el escritorio.
tjgoodman
Además, el cálculo de la altura también se puede adjuntar al evento de cambio de tamaño, pero luego podemos encontrar el mismo problema exacto de reajuste y saltos en dispositivos móviles. Por lo tanto, he encontrado que esta táctica es práctica.
tjgoodman
1
@Paul Si los navegadores de escritorio Chrome y Firefox tienen inicio táctil, ¿entonces seguramente es la característica incorrecta para detectar un navegador móvil? Acabo de probarlo en Chrome y Firefox en Windows, y ambos pasan esta prueba
Drenai
2

El siguiente código resolvió el problema (con jQuery).

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

Y los otros elementos se usan %como una unidad para reemplazar vh.

Rayo 1422
fuente
2

Aquí hay una solución que usé para mi aplicación React.

iPhone 11 Pro y iPhone Pro Max - 120px

iPhone 8 - 80px

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

Es un compromiso pero una solución relativamente simple

Trevor Wood
fuente
1

Lo siguiente funcionó para mí:

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%;
}

El bodytomará la altura de visualización visible y #your-app-containercon height: 100%hará que el contenedor de tomar la altura ventanas visibles.

Rico Kahler
fuente
0

Debido a que no se ha solucionado, puede hacer algo como:

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

# css
.content {
  height: 80vh;
}

Para mí fue la solución más rápida y más pura que jugar con JavaScript, que no podía funcionar en muchos dispositivos y navegadores.

Simplemente use el valor adecuado vhque se ajuste a sus necesidades.

turkus
fuente
0

El uso de vh en dispositivos móviles no funcionará con 100vh, debido a que sus opciones de diseño utilizan toda la altura del dispositivo sin incluir ninguna barra de direcciones, etc.

Si está buscando un diseño que incluya alturas div proporcionales a la altura de la vista real, uso la siguiente solución de CSS puro:

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

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

Tiene dos opciones para configurar la altura de la ventana gráfica, establezca manualmente --devHeight en una altura que funcione (pero deberá ingresar este valor para cada tipo de dispositivo para el que está codificando)

o

Use javascript para obtener la altura de la ventana y luego actualice --devheight al cargar y actualizar la ventana gráfica (sin embargo, esto requiere el uso de javascript y no es una solución pura de CSS)

Una vez que obtenga la altura de vista correcta, puede crear múltiples divisiones en un porcentaje exacto de la altura total de la vista simplemente cambiando el multiplicador en cada división a la que asigna la altura.

0.10 = 10% de la altura de la vista 0.57 = 57% de la altura de la vista

Espero que esto pueda ayudar a alguien;)

Ray Andison
fuente
1
Esa es una buena solución. Puede leer más sobre esto en este artículo sobre Css-tricks .
Amaury Hanser
0

Puede hacer esto agregando el siguiente guión y 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
fuente
0

Pruebe html, body { height: 100% }algo en el sentido de 100vh en dispositivos móviles.

Sam Bokai
fuente
-1

Puedes intentar darle position: fixed; top: 0; bottom: 0;propiedades a tu contenedor.

Hasan upinion
fuente
2
Agregue más información sobre por qué esta es una respuesta a la pregunta.
Drake infectado