web-dev-qa-db-fra.com

Dernière marge / remplissage se repliant dans flexbox / disposition de la grille

J'ai une liste d'éléments que j'essaie d'organiser dans une disposition horizontale déroulante avec flexbox.

Chaque élément du conteneur a une marge gauche et droite, mais la marge droite du dernier élément est réduite.

Existe-t-il un moyen d'arrêter cela ou une bonne solution de contournement?

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 300px;
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  margin: 0 30px;
  white-space: nowrap;
  flex-basis: auto;
}
<div class="container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>
30
Kevin Wilson

Problème potentiel n ° 1

La dernière marge n'est pas effondrée. Il est ignoré.

La propriété overflow s'applique uniquement au contenu . Elle ne s'applique pas au rembourrage ni aux marges.

Voici ce qu'il dit dans la spécification:

11.1.1 Débordement: la propriété overflow

Cette propriété spécifie si le contenu d'un élément de conteneur de bloc est découpé lorsqu'il déborde de la boîte de l'élément.

Jetons maintenant un œil au CSS Box Model :

enter image description here

source: W3C

La propriété overflow est limitée à la zone de zone de contenu. Si le contenu déborde de son conteneur, alors overflow s'applique. Mais overflow n'entre pas dans les zones de remplissage ou de marge (sauf, bien sûr, s'il y a plus de contenu qui suit).


Problème potentiel n ° 2

Le problème avec le problème potentiel n ° 1 est qu'il semble s'effondrer en dehors d'un contexte de formatage de flex ou de grille. Par exemple, dans une disposition de bloc standard, la dernière marge ne semble pas se réduire. Il est donc possible que overflow soit autorisé à couvrir les marges/rembourrages, indépendamment de ce qu'il indique dans la spécification.

div {
  height: 150px;
  overflow: auto;
  width: 600px;
  background: orange;
  white-space: nowrap;
}
span {
  background: blue;
  color: #fff;
  padding: 50px;
  margin: 0 30px;
  display: inline-block;
}
<div class="container">
    <span>Item 1</span>
    <span>Item 2</span>
    <span>Item 3</span>
    <span>Item 4</span>
</div>

Par conséquent, le problème est peut-être plutôt lié à des éléments qui sont "trop ​​contraints".

10.3.3 Éléments de niveau bloc non remplacés dans un flux normal

Les contraintes suivantes doivent tenir parmi les valeurs utilisées des autres propriétés:

margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right = largeur du bloc conteneur

Si width n'est pas auto et border-left-width + padding-left + width + padding-right + border-right-width (plus l'un des margin-left ou margin-right qui ne sont pas auto) est plus grande que la largeur du bloc conteneur, puis toutes les valeurs auto pour margin-left ou margin-right sont, pour les règles suivantes, traitées comme nulles.

Si tous les éléments ci-dessus ont une valeur calculée autre que auto, les valeurs sont dites "sur-contraintes" et l'une des valeurs utilisées aura être différent de sa valeur calculée. Si la propriété direction du bloc conteneur a la valeur ltr, la valeur spécifiée de margin-right est ignoré et la valeur est calculée de manière à rendre l'égalité vraie. Si la valeur de direction est rtl, ceci arrive à margin-left au lieu

(pas d'italique dans l'original)

Ainsi, selon le CSS Visual Formatting Model , les éléments peuvent être "sur-contraints" et, par conséquent, une marge droite est rejetée.


Solutions de contournement potentielles

Au lieu de marge ou de remplissage, utilisez une bordure droite sur le dernier élément:

li:last-child {
  border-right: 30px solid orange;
}
ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 100px; /* adjusted for demo */
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  margin: 0 30px;
  white-space: nowrap;
  flex-basis: auto;
}
li:last-child {
  border-right: 30px solid orange;
}
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
</ul>

Une autre solution utilise un pseudo-éléments au lieu des marges ou du remplissage.

Les pseudo-éléments d'un conteneur flexible sont rendus sous forme d'éléments flexibles. Le premier élément du conteneur est ::before et le dernier élément est ::after.

ul::after {
  content: "";
  flex: 0 0 30px;
}
ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 100px; /* adjusted for demo */
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  margin: 0 30px;
  background: blue;
  color: #fff;
  padding: 90px;
  white-space: nowrap;
  flex-basis: auto;
}
ul::after {
  content: "";
  flex: 0 0 30px;
}

ul::before {
  content: "";
  flex: 0 0 30px;
}
<ul>
  <li>Item 1</li>
  <li>Item 2</li>
  <li>Item 3</li>
  <li>Item 4</li>
</ul>
34
Michael_B

Votre problème n'est pas la marge en soi. Il s'agit de la barre de défilement dimensionnant uniquement le contenu visible de l'élément.

Un hack pour le résoudre serait de créer un élément visible qui occupe la marge

Cette solution gère cela en utilisant un pseudo sur le dernier enfant

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  display: flex;
  height: 300px;
  overflow: auto;
  width: 600px;
  background: orange;
}
ul li {
  background: blue;
  color: #fff;
  padding: 90px;
  margin: 0 30px;
  white-space: nowrap;
  flex-basis: auto;
  position: relative;
}
li:last-child:after {
  content: "";
  width: 30px;
  height: 1px;
  position: absolute;
  left: 100%;
  top: 0px;
}
<div class"container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>
5
vals

Vous pouvez définir width et overflow sur le conteneur div et définir display: inline-flex plutôt que flex sur le ul, de sorte que la taille de la boîte flexible sera calculée en fonction des éléments à l'intérieur, et tout le rembourrage et la marge s'appliqueront sans aucun problème.

.container {
  width: 600px;
  overflow: auto;
}

.container ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: inline-flex;
  background: orange;
}

.container li {
  padding: 60px;
  margin: 0 30px;
  white-space: nowrap;
  background: blue;
  color: #fff;
}
<div class="container">
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
  </ul>
</div>
3
Stickers