Cómo centrar un contenedor flexible pero alinear elementos flexibles a la izquierda

91

Quiero que los elementos flexibles estén centrados, pero cuando tenemos una segunda línea, tener 5 (de la imagen de abajo) debajo de 1 y no centrados en el padre.

ingrese la descripción de la imagen aquí

Aquí hay un ejemplo de lo que tengo:

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
fuente

Respuestas:

79

Desafío y limitación de Flexbox

El desafío es centrar un grupo de elementos flexibles y alinearlos a la izquierda en la envoltura. Pero a menos que haya un número fijo de cajas por fila, y cada caja tenga un ancho fijo, esto no es posible actualmente con flexbox.

Usando el código publicado en la pregunta, podríamos crear un nuevo contenedor flexible que envuelva el contenedor flexible actual ( ul), lo que nos permitiría centrar ulcon justify-content: center.

Luego, los elementos flexibles del ulpodría alinearse a la izquierda con justify-content: flex-start.

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

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

Esto crea un grupo centrado de elementos flexibles alineados a la izquierda.

El problema con este método es que en ciertos tamaños de pantalla habrá un espacio a la derecha del ul, por lo que ya no aparecerá centrado.

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Esto sucede porque en el diseño flexible (y, de hecho, CSS en general) el contenedor:

  1. no sabe cuándo se envuelve un elemento;
  2. no sabe que un espacio previamente ocupado ahora está vacío, y
  3. no recalcula su ancho para encoger el diseño más estrecho.

La longitud máxima del espacio en blanco de la derecha es la longitud del elemento flexible que el contenedor esperaba que estuviera allí.

En la siguiente demostración, al cambiar el tamaño de la ventana horizontalmente, puede ver que los espacios en blanco van y vienen.

MANIFESTACIÓN


Un enfoque más práctico

El diseño deseado se puede lograr sin el uso de flexbox inline-blocky las consultas de medios .

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

El código anterior representa un contenedor centrado horizontalmente con elementos secundarios alineados a la izquierda como este:

ingrese la descripción de la imagen aquí

MANIFESTACIÓN


Otras opciones

Michael Benjamin
fuente
3
Sí, mucho de lo que la gente quiere hacer y termina usando flexbox es simplemente crear una cuadrícula. Afortunadamente, esto será cubierto por CSS Grids cuando los alcances estén más finalizados.
TylerH
1
Vinculó esta publicación a la suya. Tengo una versión más de cómo resolver el mismo problema, aún así, podría ser una trampa, así que eche un vistazo y decida usted mismo si desea cerrarlo como tal. Y más 1 por la gran explicación en esto.
Ason
Quizás otra idea sea hacer margin:0 autoen una envoltura principal, para que todo esté centrado en la página. Desde ese punto, ¿todos los componentes secundarios (es decir, el primer contenedor flexible) siguen su proceso de fila de flujo normal? Parece que esto podría simplificarse.
klewis
12

Puedes lograrlo con CSS Grid, solo usa 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
fuente
Interesante. Pero no elimina la brecha a la derecha ni en el centro de la parrilla
Rojo
2
@Red Había una cosa mal. Cambié justify-items: centera justify-content:center, y ahora se centra perfectamente.
Joe82
No entiendo la 210pxparte de adentro minmax. ¿Cómo puedo hacer lo mismo pero sin una restricción artificial de 210px? No existe tal restricción con flexbox. Quiero exactamente el mismo resultado que con flexbox, pero el área de contenido debe estar centrada como en su solución.
Andrey Mikhaylov - lolmaus
1
Hola @ AndreyMikhaylov-lolmaus, este método funciona cuando quieres establecer un ancho fijo para todos los elementos. En la pregunta, se expresa como width:200px, y en mi ejemplo, como establecí width:210px, tengo que agregar eso también en la minmaxparte
Joe82
@ Joe82 ¡Gracias por aclararme! 🙏 ¿Conoce alguna forma de hacerlo funcionar para elementos de ancho arbitrario?
Andrey Mikhaylov - lolmaus
0

Como sugirió @michael, esta es una limitación con el flexbox actual. Pero si aún desea usar flexy justify-content: center;, entonces podemos solucionar esto agregando un lielemento ficticio y asignar 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 violín (cambie el tamaño y vea) o el video como referencia

RameshVel
fuente
0

Una forma de obtener el estilo deseado con márgenes es hacer lo siguiente:

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

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

Puede colocar elementos invisibles con la misma clase que los demás (eliminados en el ejemplo con fines de exhibición) y la altura establecida en 0. Con eso, podrá justificar los elementos hasta el comienzo de la cuadrícula.

Ejemplo

<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

ingrese la descripción de la imagen aquí

Arthur Serafim
fuente
-1

Me encontré con este problema mientras codificaba con React Native. Existe una solución elegante que puede tener con FlexBox. En mi situación particular, estaba tratando de centrar tres cajas flexibles (Flex: 2) dentro de otra usando alignItems. La solución que se me ocurrió fue usar dos s vacíos, cada uno con Flex: 1.

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

Bastante fácil de convertir a web / CSS.

Familia tipográfica
fuente
Has entendido mal la pregunta. El OP no pregunta cómo centrar 3 elementos, pregunta cómo alinear a la izquierda los elementos en una segunda fila mientras todos los elementos se centran como un grupo, y para eso su solución no funciona.
Ason