web-dev-qa-db-fra.com

Comment faire pour que les images restent dans les lignes d'un conteneur de grille css?

Le code ci-dessous montre le comportement souhaité lorsque je redimensionne la fenêtre dansChrome 60 et dans Firefox 55 (mais pas dans iOS Safari 10.3; c’est probablement une autre question qui explique pourquoi elle se comporte mal dans Safari). 

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background-color: lightgrey;
}

.container {
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, calc((60vh - 12px)/3));
  /*grid-template-rows: 1fr 1fr 1fr;*/
  /*grid-template-rows: auto auto auto;*/
  height: 60vh;
  border: 3px solid yellow;
  padding: 3px;
  /*grid-gap: 20px;*/ /* <-- would also mess things up */
}

.tile {
}

img {
  box-sizing: border-box;
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  padding: 3px;
}
 <!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
 <div class="container">
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
 </div>

Il est important que le format des images (2: 1)

 dummy image

est préservé. Je m'attendais soit:

grid-template-rows: 1fr 1fr 1fr;

ou:

grid-template-rows: auto auto auto;

fait que les images tiennent dans les lignes de la grille, mais aucune d’elles ne le fait . Avec:

grid-template-rows: repeat(3, calc((60vh - 12px)/3));

Je reçois le comportement souhaité. Comment puis-je éviter de faire le calcul moi-même? En d’autres termes, que dois-je faire pour que grid-template-rows: 1fr 1fr 1fr; (ou quelque chose de similaire) fonctionne?

Il est déjà difficile de calculer la hauteur de l'élément conteneur en CSS sur la page réelle. Le but est de le résoudre avec la disposition en grille CSS; pas de JavaScript, et pas de piratage de l'image de fond.


Mise à jour: À l'origine, j'avais exclu le piratage de l'image d'arrière-plan pour deux raisons. 

  1. J'ai pensé (en raison de malentendus) que l'image d'arrière-plan Url doit figurer dans le fichier CSS, mais ce n'est pas le cas: je peux utiliser les styles inline Et l'afficher au format HTML.

  2. Cela semblait hackish. Après avoir vu à quel point cela devenait compliqué et désordonné Avec des conteneurs flexibles imbriqués imbriqués dans un conteneur de grille juste pour le faire fonctionner sur Safari, j’ai simplement eu recours au hack d’image d’arrière-plan car il est significativement plus propre et plus fonctionnel dans tous les navigateurs testés (Chrome, Firefox, Safari).

En fin de compte, ce n'est pas la réponse acceptée qui a aidé à résoudre mon problème.

8
Ali

Les images sont définies sur height: 100%. Mais 100% de quoi? 100% du conteneur? 100% de la fenêtre? 100% de la ligne? Si oui, quelle est la hauteur de la rangée?

Chrome et Firefox font une supposition éclairée sur vos intentions. Ils ont mis en œuvre des algorithmes conçus pour aller au-delà des spécifications afin d'améliorer l'expérience utilisateur. Ils appellent ces modifications "interventions" .

Safari ne fait pas ça. Safari adhère strictement au langage des spécifications, qui stipule qu'un pourcentage de hauteur sur un élément doit avoir une hauteur définie sur le parent, sinon il est ignoré.

Ces différences de navigateur sont expliquées plus en détail ici: 

Ensuite, vous devez considérer que les éléments de la grille, par défaut, ne peuvent pas être plus petits que leur contenu. Si vos lignes sont définies sur 1fr, mais que les images sont plus grandes que l'espace alloué, elles doivent être développées. Vous pouvez remplacer ce comportement par min-height: 0/min-width: 0 ou overflow par n'importe quelle valeur autre que visible.

Ce comportement est expliqué plus en détail ici:

Néanmoins, une fois que vous avez pris en compte les indications ci-dessus, vous pouvez probablement utiliser votre mise en page dans Safari avec une combinaison des propriétés grid et flex:

* {
  box-sizing: border-box;
}

body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  margin: 0;
  background-color: lightgrey;
}

header,
footer {
  flex: 0 0 100px;
  background-color: tomato;
  display: flex;
  align-items: center;
  justify-content: center;
}

.container {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: auto;
  padding: 3px;
}

.tile {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 0;
}

img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  padding: 3px;
}
<header>HEADER</header>
<!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
<div class="container">
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
  <div class="tile">
    <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
  </div>
</div>
<footer>FOOTER</footer>

jsFiddle

2
Michael_B

Vous pouvez utiliser grid-template-rows: 1fr 1fr 1fr et, plus important encore, vous devez réinitialiser les valeurs min-width et min-height des éléments grid qui sont définies par défaut sur auto (autant que le contenu).

Pour fournir une taille minimale par défaut plus raisonnable pour les éléments de la grille, ceci spécification définit que la valeur automatique min-width/min-height également applique une taille minimale automatique dans l’axe spécifié aux éléments de la grille dont le débordement est visible et qui couvre au moins une piste dont min la fonction de dimensionnement de piste est auto. (L'effet est analogue à la taille minimale automatique Imposée aux éléments flexibles.)

Source: W3C

Ceci est similaire à la règle auto flex item avec flexboxes. Voir la démo ci-dessous où je les réinitialise à zéro:

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background-color: lightgrey;
}

.container {
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  height: 60vh;
  border: 3px solid yellow;
  padding: 3px;
  /*grid-gap: 20px;*/ /* <-- would also mess things up */
}

.tile {
  min-width: 0;
  min-height: 0;
}

img {
  box-sizing: border-box;
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  padding: 3px;
}
<!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
 <div class="container">
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
 </div>

3
kukkuz

Je ne sais pas à quel point vous souhaitez que les images s'adaptent, mais vous pouvez utiliser minmax() . minmax() vous permet de définir une valeur minimale et maximale pour la taille des lignes de la grille. Définir auto pour le min et 33% pour le max leur permettra d'être aussi petits que le contenu doit l'être et jusqu'à 33% de la hauteur du conteneur de grille, mais pas plus gros. Ainsi, tous vos éléments de grille resteront ensemble à une hauteur maximale de 99% des 60vh utilisés par le conteneur de grille. 

Ce n'est pas exactement la manière automatique que vous espériez obtenir ... vous déclarez toujours une taille, même si elle est relative. Cela évite la calc((60vh - 12px) / 3) d'apparence maladroite, bien qu'il n'y ait rien de mal à utiliser cette méthode, à moins que votre message ne contienne d'autres contraintes.

Cependant, la réponse de Kukkuz et la réinitialisation du min-height est une meilleure solution et c'est ce qui me manquait.

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background-color: lightgrey;
}
.container {
  box-sizing: border-box;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: repeat(3, minmax(auto, 33%));
  height: 60vh;
  border: 3px solid yellow;
  padding: 3px;
}
.tile {
    display: grid;
}
img {
  box-sizing: border-box;
  display: block;
  object-fit: contain;
  width: 100%;
  height: 100%;
  margin: 0;
  border: 0;
  padding: 3px;
}
 <!-- The image is 200 x 100 px: a green and a blue square next to each other. -->
 <div class="container">
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
   <div class="tile">
     <img src="https://i.stack.imgur.com/qbpIG.png" alt="." />
   </div>
 </div>

1
TylerH