Chrome / Safari ne remplit pas 100% de la hauteur du parent flexible

235

Je veux avoir un menu vertical avec une hauteur spécifique.

Chaque enfant doit remplir la hauteur du parent et avoir un texte aligné au milieu.

Le nombre d'enfants est aléatoire, je dois donc travailler avec des valeurs dynamiques.

Div .containercontient un nombre aléatoire d'enfants ( .item) qui doivent toujours remplir la hauteur du parent. Pour y parvenir, j'ai utilisé flexbox.

Pour créer des liens avec du texte aligné au milieu, j'utilise la display: table-celltechnique. Mais l'utilisation des affichages de table nécessite l'utilisation d'une hauteur de 100%.

Mon problème est que cela .item-inner { height: 100% }ne fonctionne pas dans le webkit (Chrome).

  1. Existe-t-il une solution à ce problème?
  2. Ou existe-t-il une technique différente pour que tous .itemremplissent la hauteur du parent avec le texte aligné verticalement au milieu?

Exemple ici jsFiddle, devrait être visualisé dans Firefox et Chrome

.container {
  height: 20em;
  display: flex;
  flex-direction: column;
  border: 5px solid black
}
.item {
  flex: 1;
  border-bottom: 1px solid white;
}
.item-inner {
  height: 100%;
  width: 100%;
  display: table;
}
a {
  background: orange;
  display: table-cell;
  vertical-align: middle;
}
<div class="container">
  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>
</div>

Ricardo Castañeda
la source
1
il a maintenant été corrigé - bugs.chromium.org/p/chromium/issues/detail?id=426898
vsync
3
Ce n'est pas spécifiquement pertinent pour OP, mais pourrait être pertinent pour les googleurs qui atterrissent ici pour "l'affichage safari, la hauteur absolue 100% ne fonctionne pas" ou quelque chose de similaire. J'ai un élément comme celui-ci dans un conteneur flexible et je devais le spécifier top:0et left:0le faire apparaître comme prévu.
AlexMA
1
@vsync n'a pas encore été corrigé: stackoverflow.com/questions/46226298/…
TylerH

Réponses:

408

Solution

Utilisez des conteneurs flex imbriqués.

Débarrassez-vous des hauteurs de pourcentage. Débarrassez-vous des propriétés de la table. Débarrassez-vous vertical-align. Évitez le positionnement absolu. Restez avec Flexbox tout au long.

Appliquer display: flexà l'élément flexible ( .item), ce qui en fait un conteneur flexible. Cela définit automatiquement align-items: stretch, qui indique à l'enfant ( .item-inner) d'étendre la hauteur totale du parent.

Important: supprimez les hauteurs spécifiées des éléments flexibles pour que cette méthode fonctionne. Si un enfant a une hauteur spécifiée (par exemple height: 100%), alors il ignorera la align-items: stretchvenue du parent. Pour que la stretchvaleur par défaut fonctionne, la taille de l'enfant doit être calculée à auto (explication complète ).

Essayez ceci (pas de modification du HTML):

Démo jsFiddle


Explication

Mon problème est que cela .item-inner { height: 100% }ne fonctionne pas dans le webkit (Chrome).

Cela ne fonctionne pas car vous utilisez le pourcentage de hauteur d'une manière qui n'est pas conforme à l'implémentation traditionnelle de la spécification.

10.5 Hauteur du contenu: la heightpropriété

pourcentage
Spécifie un pourcentage de hauteur. Le pourcentage est calculé par rapport à la hauteur du bloc contenant la boîte générée. Si la hauteur du bloc conteneur n'est pas spécifiée explicitement et que cet élément n'est pas positionné de manière absolue, la valeur est calculée en auto.

auto
La hauteur dépend des valeurs des autres propriétés.

En d'autres termes, pour que le pourcentage de hauteur fonctionne sur un enfant entrant, le parent doit avoir une hauteur définie.

Dans votre code, le conteneur de niveau supérieur a une hauteur définie: .container { height: 20em; }

Le conteneur de troisième niveau a une hauteur définie: .item-inner { height: 100%; }

Mais entre eux, le conteneur de deuxième niveau - .item- n'a pas de hauteur définie. Webkit voit cela comme un lien manquant.

.item-innerdit à Chrome: donnez-moiheight: 100% . Chrome regarde le parent ( .item) pour référence et répond: 100% de quoi? Je ne vois rien (en ignorant la flex: 1règle qui existe). En conséquence, il s'applique height: auto(hauteur du contenu), conformément à la spécification.

Firefox, d'autre part, accepte désormais la hauteur de flexion d'un parent comme référence pour le pourcentage de taille de l'enfant. IE11 et Edge acceptent également les hauteurs flexibles.

En outre, Chrome acceptera flex-growcomme référence parent adéquate s'il est utilisé en conjonction avecflex-basis (toute valeur numérique fonctionne ( autone le sera pas), y compris flex-basis: 0). Cependant, à ce jour, cette solution échoue dans Safari.


Quatre solutions

1. Spécifiez une hauteur sur tous les éléments parents

Une solution multi-navigateur fiable consiste à spécifier une hauteur sur tous les éléments parents. Cela empêche les liens manquants, que les navigateurs Webkit considèrent comme une violation de la spécification.

Notez que min-heightet max-heightne sont pas acceptables. Ce doit être la heightpropriété.

Plus de détails ici: Utilisation de la heightpropriété CSS et des valeurs de pourcentage

2. Positionnement relatif et absolu CSS

S'applique position: relativeau parent et position: absoluteà l'enfant.

Taille l'enfant height: 100%et width: 100%, ou utiliser les propriétés offset: top: 0, right: 0, bottom: 0, left: 0.

Avec un positionnement absolu, la hauteur en pourcentage fonctionne sans hauteur spécifiée sur le parent.

3. Supprimez les conteneurs HTML inutiles (recommandé)

Y a-t-il besoin de deux conteneurs autour button? Pourquoi ne pas supprimer .itemou .item-inner, ou les deux? Bien que les buttonéléments échouent parfois en tant que conteneurs flexibles , ils peuvent être des éléments flexibles. Envisagez de faire buttonun enfant de .containerou .itemet de supprimer la majoration gratuite.

Voici un exemple:

4. Conteneurs Flex imbriqués (recommandé)

Débarrassez-vous des hauteurs de pourcentage. Débarrassez-vous des propriétés de la table. Débarrassez-vous vertical-align. Évitez le positionnement absolu. Restez avec Flexbox tout au long.

Appliquer display: flexà l'élément flexible ( .item), ce qui en fait un conteneur flexible. Cela définit automatiquement align-items: stretch, qui indique à l'enfant ( .item-inner) d'étendre la hauteur totale du parent.

Important: supprimez les hauteurs spécifiées des éléments flexibles pour que cette méthode fonctionne. Si un enfant a une hauteur spécifiée (par exemple height: 100%), alors il ignorera la align-items: stretchvenue du parent. Pour que la stretchvaleur par défaut fonctionne, la taille de l'enfant doit être calculée à auto (explication complète ).

Essayez ceci (pas de modification du HTML):

jsFiddle

Michael Benjamin
la source
3
J'ai résolu mon problème en utilisant la hauteur: auto
Alfrex92
juste une remarque tirée du lien d'explication de ce grand article - "align-items: stretch" permet aux enfants flex de s'étendre sur l'axe transversal du conteneur - c'est-à-dire d'étirer les enfants à la hauteur, le conteneur flex devrait avoir une direction flexible comme ligne.
littlewhywhat
3
Je peux confirmer que l'utilisation de conteneurs flex imbriqués fonctionne mieux. Nous avons utilisé flexbox de manière incohérente dans notre menu. Au lieu d'utiliser flex: 1 et display: flex sur les conteneurs parents, nous avons utilisé la hauteur: 100% à certains endroits. Tout a fonctionné comme prévu après avoir remplacé les définitions correspondantes. Merci @Michael_B pour l'astuce! :)
flocbit
1
@JamesHarrington, CSS a grandi avec le temps et est complexe ;-)
DerMike
Chaque solution doit être une réponse différente. Comment puis-je voter dans une solution spécifique de cette façon?
Jp_
14

Solution: Retirer height: 100%en .item-intérieur et ajouter display: flexà .item

Démo: https://codepen.io/tronghiep92/pen/NvzVoo

nghiepit
la source
Merci, cela m'a aidé. Les enfants flexibles s'étirent-ils automatiquement à la hauteur de leur contenant? Est-ce pourquoi cela fonctionne?
Reece Kenney
@ReeceKenney, oui, à vos questions. Ceci est expliqué dans ma réponse. Voir la solution # 4.
Michael Benjamin
10

La spécification d'un attribut flex au conteneur a fonctionné pour moi:

.container {
    flex: 0 0 auto;
}

Cela garantit que la hauteur est définie et ne croît pas non plus.

huseyin39
la source
6

Pour Mobile Safari, il existe un correctif du navigateur. vous devez ajouter -webkit-box pour les appareils iOS.

Ex.

display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;

si vous utilisez une align-items: stretch;propriété pour l'élément parent, supprimez l' height : 100%élément de l'élément enfant.

Narasinghe
la source
2

J'ai eu un problème similaire dans iOS 8, 9 et 10 et les informations ci-dessus n'ont pas pu le résoudre, mais j'ai découvert une solution après une journée de travail. Certes, cela ne fonctionnera pas pour tout le monde, mais dans mon cas, mes articles étaient empilés dans une colonne et avaient 0 hauteur alors qu'il aurait dû être la hauteur du contenu. Changer le CSS pour être en ligne et envelopper a résolu le problème. Cela ne fonctionne que si vous avez un seul élément et qu'ils sont empilés, mais comme cela m'a pris un jour pour le découvrir, j'ai pensé que je devrais partager ma solution!

.wrapper {
    flex-direction: column; // <-- Remove this line
    flex-direction: row; // <-- replace it with
    flex-wrap: wrap; // <-- Add wrapping
}

.item {
    width: 100%;
}
Designer023
la source
0

J'ai eu un bug similaire, mais en utilisant un nombre fixe pour la hauteur et non un pourcentage. C'était aussi un conteneur flexible à l'intérieur du corps (qui n'a pas de hauteur spécifiée). Il est apparu que sur Safari, mon conteneur flexible avait une hauteur de 9 pixels pour une raison quelconque, mais dans tous les autres navigateurs, il affichait la bonne hauteur de 100 pixels spécifiée dans la feuille de style.

J'ai réussi à le faire fonctionner en ajoutant à la fois les propriétés heightet min-heightà la classe CSS.

Les éléments suivants ont fonctionné pour moi sur Safari 13.0.4 et Chrome 79.0.3945.130:

.flex-container {
  display: flex;
  flex-direction: column;
  min-height: 100px;
  height: 100px;
}

J'espère que cela t'aides!

Carlton Lindsay
la source