CSS3 100vh no constante en el navegador móvil


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

  • todo el navegador tiene un menú superior cuando se carga la página (mostrando la barra de direcciones, por ejemplo) que se desliza hacia arriba cuando se inicia el desplazamiento de la página.
  • 100vh se calculan solo en la parte visible de viewport, por lo que cuando la barra del navegador se desliza hacia arriba 100vh aumenta (en términos de píxeles)
  • todo el diseño vuelve a pintar y ajustar desde que las dimensiones han cambiado
  • mal efecto nervioso para la experiencia del usuario

¿Cómo evitar este problema? Cuando escuché por primera vez de 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 manera 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 con / sugerir una solución CSS?


Código de prueba simple:

/* maybe i can track the issue whe it occours... */
$(function(){
  var resized = -1;
  $(window).resize(function(){
    $('#currenth').val( $('.vhbox').eq(1).height() );
    if (++resized) $('#currenth').css('background:#00c');
  })
  .resize();
})
*{ margin:0; padding:0; }

/*
  this is the box which sould keep constant the height...
  min-height to allow content to be taller than viewport if too much text
*/
.vhbox{
  min-height:100vh;
  position:relative;
}

.vhbox .t{
  display:table;
  position:relative;
  width:100%;
  height:100vh;
}

.vhbox .c{
  height:100%;
  display:table-cell;
  vertical-align:middle;
  text-align:center;
}
<div class="vhbox" style="background-color:#c00">
  <div class="t"><div class="c">
  this div height should be 100% of viewport and keep this height when scrolling page
    <br>
    <!-- this input highlight if resize event is fired -->
    <input type="text" id="currenth">
  </div></div>
</div>

<div class="vhbox" style="background-color:#0c0">
  <div class="t"><div class="c">
  this div height should be 100% of viewport and keep this height when scrolling page
  </div></div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Author: Nereo Costacurta, 2016-05-09

8 answers

Desafortunadamente 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. Nos costó bastante trabajo lograr este efecto. :)

El problema de base es este: el área visible cambia dinámicamente a medida que se desplaza. Si actualizamos la altura de la ventana CSS en consecuencia, necesitamos actualice 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 tasa de fotogramas base en iOS).

Es difícil mostrarte la parte de "parece una mierda", pero imagina que a medida que te desplazas, el contenido se mueve y lo que quieres en la pantalla cambia continuamente.

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

A partir de los datos que teníamos, usar el tamaño de vista más grande era el mejor compromiso. La mayoría de los sitios web que utilizan unidades viewport se veían muy bien 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

No hay solución prevista

En este punto, no hay mucho que puede hacer excepto abstenerse de usar la altura de la ventana en dispositivos móviles. Mobile Chrome parece querer adaptar esto también, aunque no está seguro de si van a seguir a través de .

 76
Author: nils,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-09 10:50:26

Para muchos de los sitios que construyo, el cliente pedirá 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 fluida en todos los dispositivos:

Primero establezco mi elemento CSS de banner en height:100vh

Luego uso jQuery para obtener la altura en píxeles de mi elemento 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 el móvil dispositivos como cuando se carga la página, el elemento banner se establece en 100vh usando CSS y luego jQuery anula esto poniendo CSS en línea en mi elemento banner que evita que cambie de tamaño 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 banner no cambiará de tamaño porque ahora tiene una altura fija establecida en píxeles debido a la jQuery anterior. Para resolver esto, utilizo Mobile Detect para agregar una clase 'mobile' al cuerpo de mi documento. Y entonces yo envuelva el jQuery anterior en una instrucció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 'mobile' está presente en el cuerpo de mi página y se ejecuta el jQuery anterior. Por lo tanto, mi elemento 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.

 17
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-23 00:30:42

En mi aplicación lo hago así (typescript y postcss anidados, así que cambia 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 un par de veces - de alguna manera los niveles de zoom se meten con el valor innerHeight, podría publicar una actualización si encuentro una solución.

Demo

 5
Author: Andreas Herd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-04 14:56:25

Acabo de encontrar una aplicación web que diseñé 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

Salir del artículo: "simplemente haga coincidir la altura del elemento con la altura del dispositivo utilizando consultas de medios que se dirigen a las versiones anteriores de 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 CSS.

Editar pendiente: No puedo probarlo ahora mismo, pero volveré e informaré de mis resultados.

 3
Author: Jahaziel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-21 21:28:06

Se me ocurrió un componente de React – compruébelo si usa React o explore el código fuente si no lo hace, para que pueda adaptarlo a su entorno.

Establece la altura del div de pantalla completa en window.innerHeight y luego la actualiza en el cambio de tamaño de la ventana.

 2
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-28 07:37:15

@nils lo explicó claramente.

¿Qué sigue entonces?

Acabo de volver a usar relative 'classic' % (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 problemas extraños de interfaz de usuario.

 1
Author: klimat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-20 15:03:22

Como soy nuevo, no puedo comentar 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 ha funcionado bastante bien para mí y también explica el cambio de orientación móvil. Uso Jquery para el código de ejemplo, pero debería ser factible con VanillaJS.

-Primero, utilizo un script para detectar si el dispositivo es táctil o hover. Ejemplo básico:

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

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

Esto agrega clase al elemento body de acuerdo con el tipo de dispositivo (hover o touch) que se puede usar más tarde para el script height.

- Luego 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 se establece para que el dispositivo calcule correctamente la nueva altura al cambiar de orientación. Si no hay tiempo de espera, en mi experiencia la altura no será correcta. 500ms podría ser un exceso, pero ha funcionado para mí.

- 100vh en hover-dispositivos es un reserva si el navegador anula el CSS 100vh.

 0
Author: t.j.goodman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-10 09:54:52

Con suerte, esta será una variable de entorno CSS definida por UA como se sugiere aquí: https://github.com/w3c/csswg-drafts/issues/2630#issuecomment-397536046

 0
Author: Malvoz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-03 22:42:10