543 votes

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 (montrant la barre d'adresse par exemple) qui glisse vers le haut lorsque vous commencez à faire défiler la page.
  • 100vh parfois n'est calculée que sur la partie visible d'un viewport, donc lorsque la barre du navigateur glisse vers le haut, 100vh augmente (en termes de pixels)
  • toute la mise en page doit être repeinte et réajustée car les dimensions ont changé
  • un mauvais effet de saut pour l'expérience utilisateur

Comment éviter ce problème ? Quand j'ai entendu parler de viewport-height pour la première fois, j'étais enthousiaste et je pensais pouvoir l'utiliser pour des blocs de hauteur fixe au lieu d'utiliser javascript, mais maintenant je pense que le seul moyen de le faire est en fait javascript avec un événement de redimensionnement...

vous pouvez voir le problème à : site de prélèvement

Quelqu'un peut-il m'aider ou me 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 should 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>

2 votes

Si j'ai bien compris la question, le problème auquel vous êtes confronté est que dans le navigateur mobile, la hauteur est supérieure à la hauteur du viewport visible n'est-ce pas ?

0 votes

Intéressant, je n'avais jamais remarqué ça avant. C'est principalement l'image d'arrière-plan qui est sensiblement instable. Et si vous ajoutiez un transition: 0.5s ou plus, pour que le changement soit moins brutal ?

1 votes

@GauravAggarwal non, c'est exactement le contraire : la hauteur réelle du viewport est supérieure à celle fournie par le navigateur lorsque sa barre d'adresse est visible...

298voto

nils Points 11708

Malheureusement, c'est intentionnel

Il s'agit d'un problème bien connu (au moins dans safari mobile), qui est intentionnel, car il permet d'éviter d'autres problèmes. Benjamin Poulain a répondu à un bug de webkit :

C'est tout à fait 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 dynamiquement lorsque vous faites défiler la page. Si nous mettons à jour la hauteur du viewport CSS en conséquence, nous devons mettre à jour la mise en page pendant le défilement. Non seulement cela ressemble à de la merde, mais faire cela à 60 FPS est pratiquement impossible dans la plupart des pages (60 FPS est le framerate de base sur iOS).

Il est difficile de vous montrer la partie "ressemblant à de la merde", mais imaginez qu'au fur et à mesure que 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 fonctionnant pas, nous avions plusieurs choix : abandonner les unités de visualisation sur iOS, faire correspondre la taille du document comme avant iOS 8, utiliser la petite taille d'affichage, utiliser la grande taille d'affichage.

D'après les données dont nous disposions, l'utilisation d'une taille d'affichage plus grande était le meilleur compromis. La plupart des sites Web utilisant des unités d'affichage avaient une bonne apparence la plupart du temps.

Nicolas Hoizey a fait de nombreuses recherches à ce sujet : 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, il n'y a pas grand-chose que vous puissiez faire, si ce n'est vous abstenir d'utiliser la hauteur d'affichage sur les appareils mobiles. Chrome a également changé pour cela en 2016 :

0 votes

Pouvez-vous fournir un exemple avec des unités de pourcentage qui fonctionne comme le bout de code que j'ai écrit ( min-height: 100vh ) ? Merci pour la réponse... même si elle n'apporte pas de conclusion heureuse :)

0 votes

Après avoir examiné votre code plus en détail, je ne vois pas comment contourner ce problème avec des unités de pourcentage, je suis désolé.

12 votes

Yep, comme je le pense. La seule solution viable est une solution scriptée. D'après ce que je sais, $(window).height() n'est pas affecté par ce bug, donc je vais aller de ce côté. merci !

217voto

Saurabh Jain Points 1829

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

31 votes

Je quitterais min-height: 100vh; en tant que solution de repli lorsque -webkit-fill-available n'est pas pris en charge.

0 votes

@TammyTee a raison, c'est mieux de garder min-height: 100vh; car sinon, elle ne fonctionnera pas avec des navigateurs comme Firefox (du moins sur les ordinateurs de bureau, iOS utilise webkit quel que soit le navigateur).

0 votes

Cela introduit des problèmes avec le clavier sur Android Chrome. Il semble conserver sa petite hauteur une fois le clavier masqué.

79voto

Andreas Herd Points 73

Dans mon application, je le fais de la façon suivante (typescript et postcss imbriqués, donc 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);
    }
}

ça marche au moins sur chrome mobile et ipad. Ce qui ne fonctionne pas, c'est lorsque vous ajoutez votre application à l'écran d'accueil sur iOS et que vous changez l'orientation à plusieurs reprises - d'une certaine manière, les niveaux de zoom modifient la valeur innerHeight, je pourrais poster une mise à jour si je trouve une solution à ce problème.

Démo

3 votes

Je voudrais ajouter un avertissement : "@media not all and (hover:hover) {" n'est pas un moyen infaillible de détecter les navigateurs mobiles. N'hésitez pas à utiliser autre chose.

0 votes

J'aime beaucoup cette approche. La seule remarque que je voudrais faire concerne l'utilisation de l'écouteur d'événements. Cela entraîne une nouvelle peinture de la page et je ne l'utilise que dans certains scénarios, lorsqu'il est absolument essentiel que l'utilisateur voit la bonne fenêtre (par exemple, la vue initiale de la page d'accueil).

50voto

Patryk Janik Points 427

Pour moi, cette astuce a fait un travail :

height: calc(100vh - calc(100vh - 100%))

3 votes

Est-ce différent de height : 100% ?

1 votes

Oui, 100vh correspond à 100% de la hauteur d'un viewport (votre appareil), lorsque le 100% pur ne remplit que toute la zone parentale disponible (le bloc parent peut avoir par exemple 50px de hauteur et il ne remplira que cette hauteur parentale). En bref.

0 votes

Cette astuce simple a bien fonctionné, bien que la barre d'adresse ne se cache pas, mais c'est mieux que les autres solutions. upvote de ma part.

48voto

manuel-84 Points 76

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>

0 votes

Très bonne solution. Dans mon cas, je n'ai pas utilisé ponyfill, mais plutôt la logique selon laquelle pour tous les sites de bureau j'utilise 100vh, tandis que pour les mobiles j'utilise var (nous n'avons pas IE11 dans le monde mobile).

2 votes

La seule solution qui a fonctionné pour les éléments imbriqués. Si quelqu'un d'autre doit remplacer quelques centaines d'instances, vous pouvez (du moins la plupart du temps) faire correspondre la regexp (-) ?(([ \d. ]+)vh et le remplacer par calc(var(--vh) * $1$2)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X