Como centralizar um contêiner flexível, mas alinhar os itens flexíveis à esquerda

92

Eu quero que os itens flexíveis sejam centralizados, mas quando tivermos uma segunda linha, tenha 5 (da imagem abaixo) abaixo de 1 e não centralizado no pai.

insira a descrição da imagem aqui

Aqui está um exemplo do que eu tenho:

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
fonte

Respostas:

79

Desafio e limitação do Flexbox

O desafio é centralizar um grupo de itens flexíveis e alinhá-los à esquerda na embalagem. Mas, a menos que haja um número fixo de caixas por linha e cada caixa tenha largura fixa, isso não é possível com o flexbox.

Usando o código postado na pergunta, poderíamos criar um novo flex container que envolvesse o flex container atual ( ul), o que nos permitiria centralizar ulcom justify-content: center.

Em seguida, os itens flexíveis do ulpodem ser alinhados à esquerda com justify-content: flex-start.

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

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

Isso cria um grupo centralizado de flex items alinhados à esquerda.

O problema com esse método é que em certos tamanhos de tela haverá uma lacuna à direita do ul, fazendo com que ele não pareça mais centralizado.

insira a descrição da imagem aqui insira a descrição da imagem aqui

Isso acontece porque no layout flexível (e, na verdade, CSS em geral) o contêiner:

  1. não sabe quando um elemento é encerrado;
  2. não sabe que um espaço anteriormente ocupado agora está vazio, e
  3. não recalcula sua largura para reduzir o layout mais estreito.

O comprimento máximo do espaço em branco à direita é o comprimento do flex item que o contêiner esperava estar lá.

Na demonstração a seguir, redimensionando a janela horizontalmente, você pode ver o espaço em branco ir e vir.

DEMO


Uma abordagem mais prática

O layout desejado pode ser alcançado sem flexbox usando inline-blocke consultas de mídia .

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

O código acima renderiza um contêiner centralizado horizontalmente com elementos filho alinhados à esquerda como este:

insira a descrição da imagem aqui

DEMO


Outras opções

Michael Benjamin
fonte
3
Sim, muito do que as pessoas querem fazer e acabam usando o flexbox é, na verdade, apenas criar uma grade. Felizmente, isso será coberto por grades CSS quando atingir o estado mais finalizado.
TylerH
1
Vinculou esta postagem à sua. Tenho mais uma versão lá de como resolver o mesmo problema, ainda assim, pode ser uma bobagem, então dê uma olhada e decida por si mesmo se deseja encerrar como tal. E mais 1 pela ótima explicação nisso.
Ason
Talvez outra ideia seja fazer margin:0 autoem um wrapper pai, de modo que tudo fique centralizado na página. A partir desse ponto, todos os componentes filhos, (ou seja, o primeiro flex-container) passam pelo processo de linha de fluxo normal? Parece que isso poderia ser simplificado de fazer.
Klewis
13

Você pode conseguir isso com CSS Grid, basta usar 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
fonte
Interessante. Mas não elimina a lacuna à direita ou ao centro da grade
Vermelho
2
@Red Havia uma coisa errada. Eu mudei justify-items: centerpara justify-content:centere agora está centralizado perfeitamente.
Joe82 de
Eu não entendo a 210pxparte interna minmax. Como faço para fazer o mesmo, mas sem uma restrição artificial de 210px? Não existe essa restrição com o flexbox. Eu quero exatamente o mesmo resultado do flexbox, mas a área de conteúdo deve estar centralizada como na sua solução.
Andrey Mikhaylov - lolmaus
1
Olá, @ AndreyMikhaylov-lolmaus, este método funciona quando você deseja definir uma largura fixa para todos os elementos. Na pergunta, é expresso como width:200px, e no meu exemplo, conforme eu defini width:210px, eu tenho que adicionar isso também na minmaxparte
Joe82
@ Joe82 Obrigado por esclarecer! 🙏 Você conhece uma maneira de fazer funcionar para elementos de largura arbitrária?
Andrey Mikhaylov - lolmaus
0

Como @michael sugeriu, esta é uma limitação do flexbox atual. Mas se você ainda quiser usar flexe justify-content: center;, podemos contornar isso adicionando um lielemento fictício e atribuir 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); 

Verifique este violino (redimensionar e ver) ou vídeo para referência

RameshVel
fonte
0

Uma maneira de obter o estilo desejado com margens é fazer o seguinte:

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

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

Você pode colocar elementos invisíveis com a mesma classe dos demais (retirados no exemplo para fins de exibição) e altura configurada para 0. Com isso, você poderá justificar os itens logo no início da grade.

Exemplo

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

Resultado

insira a descrição da imagem aqui

Arthur Serafim
fonte
0

Eu estava enfrentando um problema semelhante ao tentar centralizar o conteúdo dentro do flex. Tentei vários truques e consertos, mas não estava funcionando por causa de um erro bobo.

Se alguém que vem aqui não consegue contornar o problema mesmo depois de ler essas respostas, tente verificar se há erros bobos. No meu caso, foi um spancom flex-growconjunto para 1adicionado como um espaçador.

mayank1513
fonte
-1

Eu tive esse problema ao codificar com o React Native. Existe uma solução elegante que você pode ter usando o FlexBox. Na minha situação particular, eu estava tentando centralizar três caixas flex (Flex: 2) dentro de outra usando alignItems. A solução que encontrei foi usar dois s vazios, cada um com Flex: 1.

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

Fácil de converter para web / CSS.

Família de fontes
fonte
Você entendeu mal a pergunta. O OP não pergunta como centralizar 3 itens, ele pergunta como alinhar os itens à esquerda em uma 2ª linha enquanto todos os itens são centralizados como um grupo, e para isso sua solução não funciona.
Ason