CSS3 100vh non constant dans le navigateur mobile


288

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 est parfois calculé uniquement sur la partie visible d'une fenêtre, donc lorsque la barre de navigation glisse vers le haut, 100vh augmente (en termes de pixels)
  • toute mise en page repeindre et réajuster puisque les dimensions ont changé
  • un mauvais effet nerveux 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 excité et j'ai pensé que je pouvais l'utiliser pour des blocs à hauteur fixe au lieu d'utiliser javascript, mais maintenant je pense que la seule façon de le faire est en fait javascript avec un événement de redimensionnement ...

vous pouvez voir le problème sur: exemple de site

Quelqu'un peut-il m'aider / proposer une solution CSS?


code de test simple:


1
si j'ai bien compris la question, le problème auquel vous êtes confronté est dans le navigateur mobile, la hauteur est plus que visible dans la fenêtre hight..right?
Gaurav Aggarwal

Intéressant, je n'ai jamais remarqué cela auparavant. C'est principalement l'image d'arrière-plan qui est visiblement nerveuse. Que diriez-vous d'ajouter un transition: 0.5sou deux, pour faire le changement moins brusquement?
C14L

@GauravAggarwal non, exactement le contraire: la hauteur réelle de la fenêtre d'affichage est supérieure à celle fournie par le navigateur lorsque sa barre d'adresse est visible ...
Nereo Costacurta

1
Puisque ma question devient populaire, je voudrais donner mes 5 cents: ne serait-il pas plus intelligent de maintenir la hauteur réelle de la fenêtre et de ne faire glisser que la barre de menus? cela ne semble pas si difficile. En fait, cela devrait être plus facile ... le doigt vers le haut -> la barre de menus glisse vers le haut jusqu'à l'invisible, le doigt vers le bas -> la barre de menus glisse vers le bas jusqu'à ce qu'elle soit complètement visible ... le tout avec le corps sans aucun effet de réajustement et de saut ...
Nereo Costacurta

4
Google a de bonnes informations à ce sujet: developers.google.com/web/updates/2016/12/url-bar-resizing Vous pouvez utiliser 100% au lieu de 100vh SI vous avez changé la hauteur du corps à 100%
Benisburgers

Réponses:


203

Malheureusement, c'est intentionnel…

Il s'agit d'un problème bien connu (au 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 pas mal 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. Si nous mettons à jour la hauteur de la fenêtre CSS en conséquence, nous devons mettre à jour la disposition 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 «ressemble à de la merde», mais imaginez que vous faites défiler le contenu et que ce que vous voulez à l'écran change constamment.

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

D'après les données dont nous disposions, utiliser la plus grande taille de vue était le meilleur compromis. La plupart des sites Web utilisant des unités de visualisation étaient superbes la plupart du temps.

Nicolas Hoizey a beaucoup étudié cela: 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 pas faire grand-chose à part vous abstenir d'utiliser la hauteur de la fenêtre d'affichage sur les appareils mobiles. Chrome a également changé en 2016:


7
Oui, comme je 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, alors je vais suivre cette voie. Merci!
Nereo Costacurta

3
Le script était le seul moyen pour moi et fonctionnait parfaitement.
captDaylight

461
Réponse la plus déprimante jamais.
Winnemucca

15
Depuis Chrome version 56, le vh est toujours calculé comme si la barre d'URL était masquée et donc vh n'augmente pas au défilement, sauf pour position:fixed. Ceci est similaire à l'implémentation sur Safari. En savoir plus: developers.google.com/web/updates/2016/12/url-bar-resizing
Kevin Farrugia

4
Dernières Chrome ne change pas vhou la <html> 100%hauteur après la charge initiale. C'est vraiment génial - comme la mise en page thrashing / redistribution lorsque la barre d'URL se cache peut être terrible. Firefoxet Edge Mobiletoujours dynamiquement mise à jour vhet <html>hauteur, provoquant beaucoup de déchirure et de redimensionnement de tous les composants lors du défilement, particulièrement mauvais si vous utilisez des SVG pour les images d'arrière-plan
Drenai

143

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


20
Je laisserais min-height: 100vh;comme solution de rechange où -webkit-fill-availablen'est pas pris en charge.
Tammy Tee

Wow merci!! Avec cela, je n'ai plus besoin de "react-div-100vh"!
Andres Elizondo

1
la réponse la plus utile!
Gauthier

@TammyTee a raison, il vaut mieux y rester min-height: 100vh;car il se briserait autrement sur les navigateurs comme Firefox (au moins sur le bureau, iOS utilise le kit Web, quel que soit le navigateur).
JoniVR

2
C'était une très bonne solution, mais semble avoir changé dans iOS 13 - Je vois de l'espace supplémentaire au bas de la première section, mais cela fonctionne exactement comme souhaité dans Safari pour iOS 12.x codepen.io/RwwL/pen / PowjvPq (il faudrait débiter ceci puis l'afficher en mode de débogage CodePen sur iOS pour vraiment voir ce que je veux dire).
RwwL

27

dans mon application, je le fais comme ça (tapuscrit 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 lorsque vous ajoutez votre application à l'écran d'accueil sur iOS et changez l'orientation plusieurs fois - en quelque sorte, les niveaux de zoom gâchent la valeur innerHeight, je pourrais publier une mise à jour si je trouve une solution.

Démo


2
Il s'agit en fait d'une approche fantastique pour un problème très ennuyeux.
Michael Giovanni Pumo

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

J'aime beaucoup cette approche. Un commentaire que je voudrais faire concerne l'utilisation de l'écouteur d'événements. Cela provoque une nouvelle peinture de la page et je ne l'utilise dans certains scénarios que lorsqu'il est absolument essentiel pour l'utilisateur de voir la fenêtre correcte (c'est-à-dire la vue initiale de la page d'accueil)
Zach Gollwitzer

22

Pour de nombreux sites que je construis, le client demandera une bannière de 100vh et, comme vous l'avez trouvé, il en résulte une mauvaise expérience "nerveuse" sur mobile lorsque vous commencez à faire défiler. Voici comment je résous le problème pour une expérience homogène et homogène sur tous les appareils:

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

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

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

Cela résout le problème sur les appareils mobiles car lorsque la page se charge, l'élément de bannière est défini sur 100vh à l'aide de CSS, puis jQuery remplace cela en mettant du CSS en ligne sur mon élément de bannière, ce qui l'empêche de se 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 il a maintenant une hauteur fixe 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'encapsule le jQuery ci-dessus dans une instruction if:

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

Par conséquent, si un utilisateur est sur un appareil mobile, la classe «mobile» est présente sur le corps de ma page et le jQuery ci-dessus est exécuté. Donc, mon élément de bannière n'aura que le CSS en ligne appliqué sur les appareils mobiles, tandis que sur le bureau, la règle CSS 100vh d'origine reste en place.


4
Je voudrais l'ajuster pour l'utiliser à la $('.banner').css({ height: window.innerHeight });place, qui est la "vraie" hauteur de la fenêtre. Exemple de fonctionnement de innerHeight
Martin

8
Ce qui est document.querySelector('.banner').style.height = window.innerHeight;destiné aux personnes écrivant du JavaScript réel.
Fabian von Ellerts

18

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>


2
solution intelligente, je rencontrais un problème avec la hauteur de la fenêtre d'affichage sur les appareils mobiles, cela devrait également être accepté comme solution (spécialement pour les appareils mobiles)
Julio Vedovatto

très belle solution. Juste dans mon cas, je n'ai pas utilisé ponyfill, mais plutôt logique que pour tous les sites de bureau j'utilise 100vh, tandis que pour mobile j'utilise var (nous n'avons pas IE11 dans le monde mobile).
FrEaKmAn

Seule solution qui fonctionnait pour les éléments imbriqués. Si quelqu'un d'autre doit remplacer quelques centaines d'instances, vous pouvez (au moins la plupart du temps) faire correspondre l'expression rationnelle (-)? ([\ D.] +) Vh et la remplacer par calc (var (- vh) * 1 $ 2 $)
ecc521

10

J'ai créé un composant React - vérifiez si vous utilisez React ou parcourez le code source si vous ne le faites pas, afin de pouvoir l'adapter à votre environnement.

Il définit la hauteur du div en plein écran, window.innerHeightpuis la met à jour lors du redimensionnement de la fenêtre.


3
Tous les héros ne portent pas de casquettes. Vous êtes bien par exemple de cela. Merci beaucoup. des heures gagnées.
NarayaN

Et pour les amoureux de Vue ... github.com/razumnyak/vue-div-100vh
Tony O'Hagan

6

Comme je cherchais une solution quelques jours, voici la mienne pour tous ceux qui utilisent VueJS avec Vuetify (ma solution utilise v-app-bar, v-navigation-tiroir et v-footer): j'ai créé App.scss (utilisé dans App. vue) avec le contenu suivant:

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

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


1
Cela fonctionne bien et peut être généralisé en dehors du monde Vue / Vuetify.
leo

5

Pour moi, une telle astuce a fait un travail:

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

Est-ce différent de la hauteur: 100%?
FF_Dev

Oui, 100vh correspond à 100% de la hauteur de la fenêtre d'affichage (votre appareil), lorsque 100% pur ne remplit que toute la zone parent disponible (le bloc parent peut avoir par exemple une hauteur de 50 pixels et il ne se remplira qu'à cette hauteur parent). En gros court.
Patryk Janik

4

@nils l'a expliqué clairement.

Et ensuite?

Je viens de revenir pour utiliser le «classique» relatif %(pourcentage) en CSS.

Il est souvent plus difficile d'implémenter quelque chose qu'il ne l'aurait utilisé vh, mais au moins, vous avez une solution assez stable qui fonctionne sur différents appareils et navigateurs sans étranges problèmes d'interface utilisateur.


1
La hauteur: 100% pour les bannières saute toujours sur les navigateurs mobiles. Je l'ai testé sur Android et jusqu'à présent, Chrome et Opera Mini donnent 100% de la hauteur de VP visible et ne changent pas en défilement. Edge et Firefox modifient la hauteur à 100% et repeignent la vue. Firefox est particulièrement mauvais, avec déchirure, rendu de texte, très évidemment re-layout
Drenai

1
@Drenai êtes-vous sûr de l'implémenter correctement? Pouvez-vous le prouver sur un petit exemple sur jsfiddle.net ou alors?
klimat

Oui, je suis sûr :-)
Drenai

3

Je viens de découvrir qu'une application Web que j'ai conçue a 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édias ciblant des appareils Apple spécifiques.

Je ne sais pas si je peux partager le code de cet article ici, mais l'adresse est la suivante: 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 avec la hauteur de l'appareil à l'aide de requêtes multimédias qui ciblent les anciennes versions de résolution iPhone et iPad."

Ils ont ajouté seulement 6 requêtes multimédias pour adapter les éléments pleine hauteur, et cela devrait fonctionner car il est entièrement implémenté CSS.

Modification en attente: je ne peux pas le tester pour le moment, mais je reviendrai et rendrai compte de mes résultats.


23
attend toujours ces résultats 😉
gman

2
Je suis désolé de ne pas être revenu comme promis. Mais après avoir manipulé le HTML et le CSS pendant beaucoup plus longtemps que je ne l'aurais souhaité, j'ai changé la conception de ma mise en page et trouvé un moyen qui fonctionnait "bien" pour mes besoins, mais qui n'était pas une solution universelle.
Jahaziel

2

Comme je suis nouveau, je ne peux pas commenter d'autres réponses.

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

-D'abord, j'utilise un script pour détecter si l'appareil est tactile ou en vol stationnaire. Exemple d'os nus:

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

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

Cela ajoute de la classe à l'élément body en fonction du type d'appareil (survol ou toucher) qui peut être utilisé ultérieurement pour le script de hauteur.

-Utilisez ensuite ce code pour définir la hauteur de l'appareil en charge et en cas de 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 de sorte que l'appareil calcule correctement la nouvelle hauteur lors d'un 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. 500 ms est peut-être exagéré mais a fonctionné pour moi.

-100vh sur hover-devices est un repli si le navigateur remplace le CSS 100vh.


2
Touchstart n'est pas pris en charge sur tous les navigateurs, et certains appareils non mobiles l'ont développeur.mozilla.org
US

@Drenai Touchstart est pris en charge sur la plupart des navigateurs mobiles. Sur le bureau, le support est un peu moindre, IE, Opera et Safari ne le prennent pas en charge. Cependant, la question d'origine s'adresse aux mobiles et non aux ordinateurs de bureau. Ce qui en fait une réponse valable.
Paul

Merci pour les commentaires! J'ai principalement utilisé cette tactique pour surmonter le réajustement et la nervosité de l'écran sur certains navigateurs mobiles. Si le navigateur ne prend pas en charge le démarrage tactile, le repli sera le CSS 100vh - si 100vh n'est pas pris en charge, c'est un sujet différent. Si le périphérique de bureau a un support tactile, la hauteur sera calculée avec javascript. Nous perdons ensuite le recalcul au redimensionnement fourni par 100vh, mais ce n'est pas un problème fatal en général car il n'y a pas de changement d'orientation sur le bureau.
tjgoodman

En outre, le calcul de la hauteur peut également être associé à l'événement de redimensionnement, mais nous pouvons alors rencontrer le même problème exact de réajustement et de nervosité sur les appareils mobiles. Par conséquent, j'ai trouvé que cette tactique était pratique.
tjgoodman

1
@Paul Si les navigateurs de bureau Chrome et Firefox ont un démarrage tactile, alors ce n'est sûrement pas la bonne fonctionnalité à utiliser pour détecter un navigateur mobile? Je viens de le tester dans Chrome et Firefox sur Windows, et tous deux réussissent ce test
Drenai

2

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é à remplacer vh.


2

Voici un travail que j'ai utilisé pour mon application React.

iPhone 11 Pro et iPhone Pro Max - 120 pixels

iPhone 8 - 80 pixels

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

C'est un compromis mais une solution relativement simple


1

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

Le bodyprendra la hauteur de la fenêtre visible et #your-app-containeravec height: 100%fera que le conteneur prendra la hauteur de la fenêtre visible.



0

Parce qu'il 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 le JavaScript qui ne pouvait pas fonctionner sur de nombreux appareils et navigateurs.

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


0

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

Si vous recherchez une disposition comprenant des hauteurs 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'affichage, définissez manuellement --devHeight sur une hauteur qui fonctionne (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 (cependant, cela nécessite l'utilisation de javascript et n'est pas une solution CSS pure)

Une fois que vous avez obtenu votre hauteur de vue correcte, vous pouvez créer plusieurs div à un pourcentage exact de la hauteur totale de la fenêtre en changeant simplement le multiplicateur de chaque div à laquelle 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;)


1
Voilà une bonne solution. Vous pouvez en savoir plus à ce sujet dans cet article sur les astuces Css .
Amaury Hanser du

0

Vous pouvez le faire en ajoutant le script et le style suivants

  function appHeight() {
    const doc = document.documentElement
    doc.style.setProperty('--vh', (window.innerHeight*.01) + 'px');
  }
  window.addEventListener('resize', appHeight);
  appHeight();

Style

.module {
 height: 100vh; /* Fallback for browsers that do not support Custom Properties */
  height: calc(var(--vh, 1vh) * 100);
}

0

Essayez html, body { height: 100% }quelque chose à l'effet de 100vh sur les appareils mobiles.


-1

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


2
Ajoutez plus d'informations sur les raisons pour lesquelles il s'agit d'une réponse à la question.
Infected Drake le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.