Comment centrer un conteneur flexible mais aligner des éléments flexibles à gauche

91

Je veux que les éléments flexibles soient centrés, mais lorsque nous avons une deuxième ligne, avoir 5 (de l'image ci-dessous) sous 1 et non centrés dans le parent.

entrez la description de l'image ici

Voici un exemple de ce que j'ai:

ul {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  margin: 0;
  padding: 0;
}
li {
  list-style-type: none;
  border: 1px solid gray;
  margin: 15px;
  padding: 5px;
  width: 200px;
}
<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
  <li>6</li>
</ul>

http://jsfiddle.net/8jqbjese/2/

Etienne Noël
la source

Réponses:

79

Défi et limitation de la Flexbox

Le défi consiste à centrer un groupe d'éléments flexibles et à les aligner à gauche sur l'enveloppe. Mais à moins qu'il n'y ait un nombre fixe de boîtes par ligne et que chaque boîte soit de largeur fixe, ce n'est actuellement pas possible avec flexbox.

En utilisant le code publié dans la question, nous pourrions créer un nouveau conteneur flex qui encapsule le conteneur flex actuel ( ul), ce qui nous permettrait de centrer le ulavec justify-content: center.

Ensuite, les éléments flexibles du ulpeuvent être alignés à gauche avec justify-content: flex-start.

#container {
    display: flex;
    justify-content: center;
}

ul {
    display: flex;
    justify-content: flex-start;
}

Cela crée un groupe centré d'éléments flexibles alignés à gauche.

Le problème avec cette méthode est qu'à certaines tailles d'écran, il y aura un espace sur la droite du ul, ce qui le fera ne plus apparaître centré.

entrez la description de l'image ici entrez la description de l'image ici

Cela se produit parce que dans la mise en page flex (et, en fait, CSS en général), le conteneur:

  1. ne sait pas quand un élément s'enroule;
  2. ne sait pas qu'un espace précédemment occupé est maintenant vide, et
  3. ne recalcule pas sa largeur pour réduire la mise en page la plus étroite.

La longueur maximale de l'espace blanc sur la droite est la longueur de l'élément flexible que le conteneur s'attendait à y trouver.

Dans la démo suivante, en redimensionnant la fenêtre horizontalement, vous pouvez voir les espaces aller et venir.

DEMO


Une approche plus pratique

La mise en page souhaitée peut être obtenue sans flexbox inline-blockni requêtes multimédias .

HTML

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
</ul>

CSS

ul {
    margin: 0 auto;                  /* center container */
    width: 1200px;
    padding-left: 0;                 /* remove list padding */
    font-size: 0;                    /* remove inline-block white space;
                                        see https://stackoverflow.com/a/32801275/3597276 */
}

li {
    display: inline-block;
    font-size: 18px;                 /* restore font size removed in container */
    list-style-type: none;
    width: 150px;
    height: 50px;
    line-height: 50px;
    margin: 15px 25px;
    box-sizing: border-box;
    text-align: center;
}

@media screen and (max-width: 430px) { ul { width: 200px; } }
@media screen and (min-width: 431px) and (max-width: 630px) { ul { width: 400px; } }
@media screen and (min-width: 631px) and (max-width: 830px) { ul { width:600px;  } }
@media screen and (min-width: 831px) and (max-width: 1030px) { ul { width: 800px; } }
@media screen and (min-width: 1031px) and (max-width: 1230px) { ul { width: 1000px; } }

Le code ci-dessus rend un conteneur centré horizontalement avec des éléments enfants alignés à gauche comme ceci:

entrez la description de l'image ici

DEMO


Autres options

Michael Benjamin
la source
3
Oui, une grande partie de ce que les gens veulent faire et qui finissent par utiliser flexbox est en fait simplement de créer une grille. Heureusement, cela sera couvert par les grilles CSS lorsque cet état sera plus finalisé.
TylerH
1
Lié ce post au vôtre. J'ai une autre version là-dedans pour résoudre le même problème, mais cela pourrait être une dupe, alors jetez un coup d'œil et décidez par vous-même si vous souhaitez le fermer en tant que tel. Et plus 1 pour la grande explication à ce sujet.
Ason le
Peut-être qu'une autre idée est de faire margin:0 autosur un wrapper parent, de sorte que tout soit centré sur la page. À partir de là, tous les composants enfants, (c'est-à-dire le premier conteneur flexible) se contentent de suivre son processus de ligne de flux normal? On dirait que cela pourrait être simple à faire.
klewis le
12

Vous pouvez y parvenir avec CSS Grid, utilisez simplement repeat(autofit, minmax(width-of-the-element, max-content))

ul {
       display: grid;
       grid-template-columns: repeat(auto-fit, minmax(210px, max-content));
       grid-gap: 16px;
       justify-content: center;
       padding: initial;
}

li {
    list-style-type: none;
    border: 1px solid gray;
    padding: 5px;
    width: 210px;
}
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
</ul>

http://jsfiddle.net/rwa20jkh/

Joe82
la source
Intéressant. Mais n'élimine pas l'écart à droite ni ne centre la grille
Rouge
2
@Red Il y avait une chose qui n'allait pas. J'ai changé justify-items: centerpour justify-content:center, et maintenant il se concentre parfaitement.
Joe82
Je ne comprends pas la 210pxpartie à l'intérieur minmax. Comment puis-je du même mais sans restriction artificielle de 210px? Il n'y a pas une telle restriction avec flexbox. Je veux exactement le même résultat qu'avec flexbox, mais la zone de contenu doit être centrée comme dans votre solution.
Andrey Mikhaylov - lolmaus le
1
Salut @ AndreyMikhaylov-lolmaus, cette méthode fonctionne lorsque vous souhaitez définir une largeur fixe pour tous les éléments. Dans la question, il est exprimé comme width:200px, et dans mon exemple, comme je l'ai indiqué width:210px, je dois ajouter cela également dans la minmaxpartie
Joe82
@ Joe82 Merci d'avoir clarifié! 🙏 Connaissez-vous un moyen de le faire fonctionner pour des éléments de largeur arbitraire?
Andrey Mikhaylov - lolmaus le
0

Comme @michael l'a suggéré, il s'agit d'une limitation avec la flexbox actuelle. Mais si vous souhaitez toujours utiliser flexet justify-content: center;, nous pouvons contourner le problème en ajoutant un liélément factice et en attribuant margin-left.

    const handleResize = () => {
        const item_box = document.getElementById('parentId')
        const list_length  = item_box.clientWidth
        const product_card_length = 200 // length of your child element
        const item_in_a_row = Math.round(list_length/product_card_length)
        const to_be_added = item_in_a_row - parseInt(listObject.length % item_in_a_row) // listObject is the total number items
        const left_to_set = (to_be_added - 1 ) * product_card_length // -1 : dummy item has width set, so exclude it when calculating the left margin 
        const dummy_product = document.querySelectorAll('.product-card.dummy')[0]
        dummy_product.style.marginLeft = `${left_to_set}px`
    }
    handleResize() // Call it first time component mount
    window.addEventListener("resize", handleResize); 

Vérifiez ce violon (redimensionner et voir) ou vidéo pour référence

RameshVel
la source
0

Une façon d'obtenir le style souhaité avec des marges est de procéder comme suit:

#container {
    display: flex;
    justify-content: center;
}

#innercontainer {
    display: flex;
    flex: 0.9; -> add desired % of margin
    justify-content: flex-start;
}
Patrik Rikama-Hinnenberg
la source
0

Vous pouvez placer des éléments invisibles avec la même classe que les autres (supprimés sur exemple à des fins d'exposition) et une hauteur fixée à 0. Avec cela, vous pourrez justifier les éléments au tout début de la grille.

Exemple

<div class="table-container">
  <div class="table-content">
    <p class="table-title">Table 1</p>
    <p class="mesa-price">$ 20</p>
  </div>
  
  <!-- Make stuff justified start -->
  <div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
  <div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
  <div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
  <div class="table-content" style="opacity: 0; cursor: default; height: 0; margin-bottom: 0;"></div>
</div>

Résultat

entrez la description de l'image ici

Arthur Serafim
la source
-1

J'ai rencontré ce problème lors du codage avec React Native. Il existe une solution élégante que vous pouvez avoir en utilisant FlexBox. Dans ma situation particulière, j'essayais de centrer trois boîtes flexibles (Flex: 2) dans une autre en utilisant alignItems. La solution que j'ai trouvée consistait à utiliser deux s vides, chacun avec Flex: 1.

<View style={{alignItems: 'center', flexWrap: 'wrap', flexDirection: 'row'}}>
  <View style={{flex: 1}} />
    // Content here
  <View style={{flex: 1}} />
</View>

Assez facile à convertir en web / CSS.

Famille de polices
la source
Vous avez mal compris la question. Le PO ne demande pas comment centrer 3 éléments, il demande comment aligner à gauche les éléments sur une deuxième ligne pendant que tous les éléments sont centrés en groupe, et pour cela, votre solution ne fonctionne pas.
Ason le