Jak wyśrodkować pojemnik elastyczny, ale wyrównać elementy elastyczne do lewej


91

Chcę, aby elementy flex były wyśrodkowane, ale gdy mamy drugą linię, aby mieć 5 (z obrazka poniżej) poniżej 1 i nie wyśrodkowane w rodzicu.

wprowadź opis obrazu tutaj

Oto przykład tego, co mam:

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/

Odpowiedzi:


79

Wyzwanie i ograniczenia dotyczące Flexbox

Wyzwaniem jest wyśrodkowanie grupy elementów elastycznych i wyrównanie ich w lewo na zawijaniu. Ale jeśli nie ma stałej liczby pudełek w rzędzie, a każde pudełko ma stałą szerokość, nie jest to obecnie możliwe w przypadku Flexbox.

Korzystając z kodu zamieszczonego w pytaniu, moglibyśmy utworzyć nowy kontener flex, który otacza obecny kontener flex ( ul), co pozwoliłoby nam wyśrodkować ulz justify-content: center.

Następnie elementy flex ulmożna wyrównać do lewej z justify-content: flex-start.

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

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

Tworzy to wyśrodkowaną grupę elementów elastycznych wyrównanych do lewej.

Problem z tą metodą polega na tym, że przy pewnych rozmiarach ekranu będzie luka po prawej stronie ul, przez co nie będzie już wyglądać na wyśrodkowaną.

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Dzieje się tak, ponieważ w układzie flex (a właściwie ogólnie w CSS) kontener:

  1. nie wie, kiedy element się zawija;
  2. nie wie, że zajmowane wcześniej miejsce jest teraz puste, i
  3. nie oblicza ponownie swojej szerokości, aby zawijać węższy układ.

Maksymalna długość odstępu po prawej stronie to długość elementu elastycznego, którego oczekiwał kontener.

W poniższym demo, zmieniając rozmiar okna w poziomie, możesz zobaczyć pojawianie się i znikanie białych znaków.

PRÓBNY


Bardziej praktyczne podejście

Żądany układ można osiągnąć bez użycia flexboxa inline-blocki zapytań o media .

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

Powyższy kod renderuje kontener wyśrodkowany w poziomie z elementami potomnymi wyrównanymi do lewej, takimi jak ten:

wprowadź opis obrazu tutaj

PRÓBNY


Inne opcje


3
Tak, wiele z tego, co ludzie chcą robić i kończą przy użyciu flexboksa, to po prostu tworzenie siatki. Na szczęście zostanie to objęte siatkami CSS, gdy osiągnie stan bardziej sfinalizowany.
TylerH,

1
Powiązałem ten post z Twoim. Mam tam jeszcze jedną wersję, jak rozwiązać ten sam problem, ale może to być oszustwo, więc spójrz i sam zdecyduj, czy chcesz to zamknąć jako takie. I plus 1 za świetne wyjaśnienie.
Ason

Być może innym pomysłem jest zrobienie margin:0 autoopakowania nadrzędnego, aby wszystko było wyśrodkowane na stronie. Od tego momentu wszystkie komponenty podrzędne (tj. Pierwszy kontener elastyczny) po prostu wykonują normalny proces przepływu wiersza? Wydaje się, że można to uprościć.
klewis

12

Możesz to osiągnąć za pomocą siatki CSS, po prostu użyj 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/


Ciekawy. Ale nie eliminuje luki po prawej ani nie wyśrodkowuje siatki
czerwony

2
@Red Jedna rzecz była nie tak. Zmieniłem się justify-items: centerna justify-content:center, a teraz idealnie się centruje.
Joe82

Nie rozumiem 210pxczęści w środku minmax. Jak zrobić to samo, ale bez sztucznego ograniczenia 210px? W przypadku flexbox nie ma takiego ograniczenia. Chcę dokładnie tego samego wyniku co w przypadku flexboksa, ale obszar zawartości musi być wyśrodkowany tak, jak w twoim rozwiązaniu.
Andrey Mikhaylov - lolmaus

1
Cześć @ AndreyMikhaylov-lolmaus, ta metoda działa, gdy chcesz ustawić stałą szerokość dla wszystkich elementów. W pytaniu jest to wyrażone jako width:200px, aw moim przykładzie, tak jak ustawiłem width:210px, muszę to dodać również w minmaxczęści
Joe82

@ Joe82 Dzięki za wyjaśnienie! 🙏 Czy znasz sposób, aby działał z elementami o dowolnej szerokości?
Andrey Mikhaylov - lolmaus

0

Jak zasugerował @michael, jest to ograniczenie w obecnym flexboksie. Ale jeśli nadal chcesz używać flexi justify-content: center;, możemy to obejść, dodając lielement fikcyjny i przypisując 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); 

Sprawdź to skrzypce (zmień rozmiar i zobacz) lub wideo w celach informacyjnych


0

Jednym ze sposobów uzyskania pożądanego stylu z marginesami jest wykonanie następujących czynności:

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

#innercontainer {
    display: flex;
    flex: 0.9; -> add desired % of margin
    justify-content: flex-start;
}

0

Możesz umieścić niewidoczne elementy z tą samą klasą co pozostałe (usunięte np. Na potrzeby wystawy) i wysokością ustawioną na 0. Dzięki temu będziesz mógł wyrównać pozycje na samym początku siatki.

Przykład

<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>

Wynik

wprowadź opis obrazu tutaj


-1

Napotkałem ten problem podczas kodowania w React Native. Jest eleganckie rozwiązanie, które możesz mieć używając FlexBox. W mojej konkretnej sytuacji próbowałem wyśrodkować trzy pola flex (Flex: 2) wewnątrz innego za pomocą alignItems. Rozwiązanie, które wymyśliłem, polegało na użyciu dwóch pustych s, każdy z Flex: 1.

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

Wystarczająco łatwe do konwersji na web / CSS.


Źle zrozumiałeś pytanie. OP nie pyta, jak wyśrodkować 3 elementy, pyta, jak wyrównać elementy w drugim rzędzie, podczas gdy wszystkie elementy są wyśrodkowane jako grupa, i dlatego twoje rozwiązanie nie działa.
Ason
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.