web-dev-qa-db-fra.com

CSS3 100vh non constant dans le navigateur mobile

J'ai un problème très étrange ... dans chaque navigateur et version mobile, j'ai rencontré ce comportement:

  • tous les navigateurs ont un menu supérieur lorsque vous chargez la page (affichant par exemple la barre d'adresse) qui glisse vers le haut lorsque vous démarrez, faites défiler la page.
  • 100vh ne sont calculés que sur la partie visible de la fenêtre. Ainsi, lorsque la barre de navigation glisse vers le haut de 100vh, le nombre de pixels augmente.
  • toute la mise en page re-Paint et re-ajuster depuis les dimensions ont changé
  • mauvais effet nerveux pour l'expérience utilisateur

comment peut éviter ce problème? Quand j'ai entendu parler de viewport-height pour la première fois, j'étais enthousiasmé et je pensais que je pouvais l'utiliser pour des blocs à hauteur fixe plutôt que d'utiliser du javascript, mais je pense que la seule façon de le faire est en fait de javascript avec un événement de redimensionnement ...

vous pouvez voir le problème à: site exemple

Quelqu'un peut-il m'aider avec/suggérer une solution CSS?


code de test 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>

133

Malheureusement, c'est intentionnel…

C’est un problème bien connu (du moins dans Safari Mobile), qui est intentionnel, car il évite d’autres problèmes. Benjamin Poulain a répondu à un bug du webkit :

C'est complètement intentionnel. Il a fallu beaucoup de travail de notre part pour obtenir cet effet. :)

Le problème de base est le suivant: la zone visible change de manière dynamique lorsque vous faites défiler. Si nous mettons à jour la hauteur de la fenêtre CSS en conséquence, nous devons mettre à jour la présentation pendant le défilement. Non seulement cela ressemble à de la merde, mais le faire à 60 FPS est pratiquement impossible dans la plupart des pages (60 FPS est le taux de rafraîchissement de base sur iOS).

Il est difficile de vous montrer la partie «qui ressemble à de la merde», mais imaginez que lorsque vous faites défiler, le contenu se déplace et ce que vous voulez à l'écran change continuellement.

La mise à jour dynamique de la hauteur ne fonctionnait pas, nous avions plusieurs choix: déposer des unités de fenêtre d'affichage sur iOS, faire correspondre la taille du document comme avant iOS 8, utiliser la petite taille de la vue, utiliser la grande taille de la vue.

D'après les données dont nous disposions, utiliser le plus grand format de vue était le meilleur compromis. La plupart des sites Web utilisant des unités de viewport étaient en grande partie très beaux.

Nicolas Hoizey a beaucoup étudié la question: https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some -mobile-browsers.html

Aucun correctif prévu

À ce stade, vous ne pouvez rien faire de plus, à moins de ne pas utiliser la hauteur de la fenêtre d'affichage sur les appareils mobiles. Mobile Chrome semble vouloir l’adapter aussi, même s’il n’est pas certain qu’ils vont suivre .

102
nils

Pour la plupart des sites que je construis, le client demande une bannière de 100vh et, comme vous l'avez déjà constaté, il en résulte une mauvaise expérience sur le mobile lorsque vous commencez à faire défiler. Voici comment résoudre le problème pour une expérience homogène et homogène sur tous les appareils:

J'ai d'abord défini mon élément de bannière CSS sur height:100vh

Ensuite, j'utilise jQuery pour obtenir la hauteur en pixels de mon élément de bannière et applique un style en ligne utilisant cette hauteur.

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

Cela résout le problème sur les appareils mobiles, car lors du chargement de la page, l'élément bannière est défini sur 100vh à l'aide de CSS, puis jQuery le remplace en mettant le code CSS en ligne sur l'élément bannière, ce qui l'empêche de redimensionner lorsqu'un utilisateur commence à faire défiler.

Cependant, sur le bureau, si un utilisateur redimensionne la fenêtre de son navigateur, mon élément de bannière ne sera pas redimensionné, car sa hauteur est maintenant définie en pixels en raison de la jQuery ci-dessus. Pour résoudre ce problème, j'utilise Mobile Detect pour ajouter une classe "mobile" au corps de mon document. Et puis j'emballe le jQuery ci-dessus dans une déclaration if:

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

En conséquence, si un utilisateur est sur un appareil mobile, la classe "mobile" est présente dans le corps de ma page et jQuery ci-dessus est exécuté. Ainsi, mon élément de bannière aura uniquement le CSS en ligne appliqué sur les appareils mobiles, tandis que sur le bureau, la règle CSS initiale de 100vh reste en place.

18
user6261119

dans mon application, je le fais comme ça (TypeScript et postcss imbriqué, alors changez le code en conséquence):

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

dans votre 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);
    }
}

cela fonctionne au moins sur chrome mobile et ipad. Ce qui ne fonctionne pas, c’est que lorsque vous ajoutez votre application à l’écran d’accueil sur iOS et que vous modifiez l’orientation à quelques reprises - les niveaux de zoom perturbent la valeur innerHeight. Je pourrais poster une mise à jour si je trouve une solution.

Démo

11
Andreas Herd

Vous pouvez essayer min-height: -webkit-fill-available; dans votre css au lieu de 100vh. Il devrait être résolu

6
Saurabh Jain

C'est le truc dont vous aurez besoin https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ .

CSS

.my-element {
    height: 100vh;                      /* Fallback for browsers that do not support Custom Properties */
    height: calc(var(--vh, 1vh) * 100);
    transition: 1s;                     /* To avoid a jumpy result */
}

JS

// 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`);
});
3
Stijn Slats

Je viens de trouver une application Web que j'ai conçue qui présente ce problème avec les iPhones et les iPads, et j'ai trouvé un article suggérant de le résoudre à l'aide de requêtes multimédia ciblant des appareils Apple spécifiques.

Je ne sais pas si je peux partager le code de cet article ici, mais voici l'adresse: http://webdesignerwall.com/tutorials/css-fix-for-ios-vh-unit-bug

Citant l'article: "il suffit de faire correspondre la hauteur de l'élément à celle de l'appareil à l'aide de requêtes multimédia ciblant les anciennes versions de la résolution pour iPhone et iPad".

Ils ont ajouté seulement 6 requêtes de média pour adapter des éléments de hauteur totale, et cela devrait fonctionner car il est complètement implémenté par CSS.

En attente de modification: je ne peux pas le tester pour le moment, mais je reviendrai et vous rapporterai mes résultats.

3
Jahaziel

@nils l'a expliqué clairement. 

Quelle est la prochaine alors?

Je suis juste retourné pour utiliser le "classique" relatif% (pourcentage) en CSS.

Il est souvent plus difficile d'implémenter quelque chose que d'utiliser vh, mais au moins, vous disposez d'une solution assez stable qui fonctionne sur différents périphériques et navigateurs sans problèmes d'interface utilisateur étranges.

3
klimat

Je suis arrivé avec un composant React - vérifiez-le si vous utilisez React ou parcourez le code source si vous ne le faites pas, vous pourrez ainsi l'adapter à votre environnement.

Il définit la hauteur de la div en plein écran sur window.innerHeight, puis la met à jour lors des redimensionnements de fenêtre.

2
Mike

Le code suivant a résolu le problème (avec jQuery). 

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

Et les autres éléments utilisent % comme une unité pour remplacer vh

2
Ray1422

Comme je suis nouvelle, je ne peux pas commenter d’autres réponses.

Si quelqu'un cherche une réponse pour que cela fonctionne (et peut utiliser le javascript - comme cela semble être nécessaire pour le faire pour le moment), cette approche a plutôt bien fonctionné pour moi et prend également en compte le changement d'orientation. J'utilise Jquery pour l'exemple de code mais cela devrait être faisable avec vanillaJS.

-Tout d'abord, j'utilise un script pour détecter si le périphérique est tactile ou en vol stationnaire. Exemple à nu:

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

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

Cela ajoute une classe à l'élément body en fonction du type de périphérique (survol ou tactile) pouvant être utilisé ultérieurement pour le script height.

-Next utilise ce code pour définir la hauteur de l'appareil en charge et lors du changement d'orientation:

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


}

-Timeout est défini pour que l'appareil calcule correctement la nouvelle hauteur lors du changement d'orientation. S'il n'y a pas de délai d'attente, d'après mon expérience, la hauteur ne sera pas correcte. 500ms peut être un excès mais a fonctionné pour moi. 

-100vh sur hover-devices est une solution de secours si le navigateur remplace le CSS 100vh.

1
t.j.goodman

Regardez cette réponse: 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>

1
manuel-84

Ce qui suit a fonctionné pour moi:

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

La body prendra la hauteur de fenêtre visible et #your-app-container avec height: 100% fera que le conteneur prendra la hauteur de fenêtre visible.

1
Rico Kahler

L'utilisation de vh sur des appareils mobiles ne fonctionnera pas avec 100vh, en raison de leurs choix de conception utilisant toute la hauteur de l'appareil, sans barres d'adresse, etc.

Si vous recherchez une mise en page incluant des hauteurs de div proportionnelles à la hauteur de vue réelle, j'utilise la solution CSS pure suivante:

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

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

Vous avez deux options pour définir la hauteur de la fenêtre. Définissez manuellement le paramètre --devHeight sur une hauteur qui convient (mais vous devrez entrer cette valeur pour chaque type de périphérique pour lequel vous codez).

ou

Utilisez javascript pour obtenir la hauteur de la fenêtre, puis mettez à jour --devheight lors du chargement et de l'actualisation de la fenêtre d'affichage.

Une fois que vous avez obtenu la hauteur de vue correcte, vous pouvez créer plusieurs div à un pourcentage exact de la hauteur totale de la fenêtre en modifiant simplement le multiplicateur de chaque div auquel vous attribuez la hauteur.

0,10 = 10% de la hauteur de vue 0,57 = 57% de la hauteur de vue

J'espère que cela pourrait aider quelqu'un;)

0
Ray Andison

Vous pouvez essayer de donner des propriétés position: fixed; top: 0; bottom: 0; à votre conteneur.

0
Hasan upinion

Parce que ça ne sera pas corrigé, vous pouvez faire quelque chose comme:

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

# css
.content {
  height: 80vh;
}

Pour moi, c’était la solution la plus rapide et la plus pure que de jouer avec JavaScript qui ne pouvait pas fonctionner sur de nombreux appareils et navigateurs. 

Utilisez simplement la valeur appropriée vh qui correspond à vos besoins.

0
turkus

J'espère que ce sera une variable d'environnement CSS définie par le UA, comme suggéré ici: https://github.com/w3c/csswg-drafts/issues/2630#issuecomment-397536046

0
Malvoz