flex-grow no dimensiona los elementos flex como se esperaba


Parece que el contenido dentro de un div flex afecta su tamaño calculado con respecto a la propiedad flex-grow. Estoy haciendo algo mal?

En el violín proporcionado a continuación, verá un teclado numérico. Todas las filas contienen 3 números excepto la fila inferior. Esa fila debe tener el ' 0 ' ser el ancho de 2 números, por lo tanto flex-grow: 2 y el ':' (dos puntos) ser el tamaño de 1 número, por lo tanto flex-grow: 1.

¿Me estoy perdiendo algo aquí?

El lado derecho del ' 0 ' debe estar alineado con el 8, 5, y 2 por encima de él. Es un poco raro.

introduzca la descripción de la imagen aquí

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-grow: 1;
    justify-content: center;
    align-items: center;
    margin: 5px;
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
}

.button#number0 {
    flex-grow: 2;
}

.button#colon {
    flex-grow: 1;
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

Https://jsfiddle.net/0r4hemdu /

Author: Michael_B, 2016-01-07

5 answers

Parece que el contenido dentro de un div flex afecta su tamaño calculado con respecto a la propiedad flex-grow. Estoy haciendo algo mal?

El origen del problema no es el contenido dentro del elemento flex.

En el violín proporcionado a continuación, verá un teclado numérico. Todas las filas contienen 3 números excepto la fila inferior. Esa fila debe tener el ' 0 ' ser el ancho de 2 números, por lo tanto flex-grow: 2 y el ':' ser el tamaño de 1 número, por lo tanto flex-grow: 1. Soy yo falta algo aquí?

Sí. Su interpretación de la propiedad flex-grow es incorrecta. flex-grow no está destinado a definir el tamaño de un elemento flexible. Su trabajo es distribuir el espacio libre en el contenedor flex entre los elementos flex.

Al aplicar flex-grow: 1 a un grupo de elementos flexibles, les estás diciendo que distribuyan el espacio libre uniformemente entre ellos. Esta es la razón por la que, en su demostración, las filas uno, dos y tres tienen elementos flexibles del mismo tamaño.

Cuando solicitas flex-grow: 2, estás decirle al elemento flexible que consuma el doble de espacio libre que los elementos con flex-grow: 1. Esto no significa necesariamente que el artículo flex sea el doble del tamaño.

También, en su caso, incluso si flex-grow: 2 significaba el doble del tamaño, ¿cómo sabría incluir ese segundo margen de 10px de las filas anteriores a su cálculo de ancho (que sería necesario para lograr la alineación que desea)? introduzca la descripción de la imagen aquí

La razón por la que la alineación está desactivada en la fila cuatro es porque solo tiene una margen de 10px.

Notará que si elimina la regla de margen obtendrá la alineación deseada.

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-grow: 1;
    justify-content: center;
    align-items: center;
    /* margin: 5px; */
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
}

.button#number0 {
    flex-grow: 2;
}

.button#colon {
    flex-grow: 1;
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

Entonces, ¿qué pasa en la fila cuatro a ese segundo margen de 10px?

Es absorbido por los dos elementos flex.

Así es como flex-grow distribuye el espacio extra en la fila cuatro:

  • Flex item left (con contenido "0") tiene flex-grow: 2. (.button#number0 en el código.)
  • Elemento flexible la derecha (con el contenido ":") tiene flex-grow: 1. (.button#colon en el código.)
  • El segundo margen entre elementos, que aparece solo en filas con tres elementos flexibles, es de 10px de ancho. (El código dice 5px alrededor de cada elemento, pero en CSS márgenes horizontales nunca collapse . Además, en flexbox, no hay márgenes collapse .)
  • La suma total de los valores flex-grow es tres. Dividamos 10px por 3. Ahora sabemos que la proporción de 1 es 3.33 px.
  • Por lo tanto, flex item left obtiene 6.66 px del espacio extra, y flex item right obtiene 3.33 px.
  • Digamos que flex item left tenía flex-grow: 3 en su lugar. Luego, el elemento flex izquierdo obtendría 7.5 px, y el elemento flex derecho obtendría 2.5 px.

La última parte de tu pregunta dice: {[31]]}

El lado derecho del '0' debe estar alineado con el 8, 5 y 2 por encima de él. Es un poco raro.

Debido a que flex-grow no está directamente involucrado en el dimensionamiento de los artículos flex, necesitará otro método.

Intenta flex-basis en su lugar. Esto parece funcionar:

.button    { flex-basis: 33.33%; }
#number0   { flex-basis: calc(66.67% + 10px); }

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-basis: 33.33%;
    justify-content: center;
    align-items: center;
    margin: 5px;
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
}

#number0 {
    flex-basis: calc(66.67% + 10px);
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

UPDATE

En respuesta a la retroalimentación en los comentarios, he hecho un ajuste al código anterior. Al agregar box-sizing: border-box, se corrige una desalineación de 1-2px.

.button    { flex-basis: 33.33%; box-sizing: border-box }
#number0   { flex-basis: calc(66.67% + 10px); }

.numbers {
    display: flex;
    flex-direction: column;
}

.row {
    display: flex;
    flex-direction: row;
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
    flex-basis: 33.33%;
    justify-content: center;
    align-items: center;
    margin: 5px;
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(255, 255, 255, 0.2);
    cursor: pointer;
    box-sizing: border-box;
}

#number0 {
    flex-basis: calc(66.67% + 10px);
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

JSFIDDLE


Referencias:

 27
Author: Michael_B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-18 21:10:34

Actualizar 3:

Descubrí otra manera de deshacerme de la desalineación.

Esta versión, junto con la actualización 2:nd, funciona con el html original intacto, y está utilizando pseudo elementos para crear los botones, efectos de botón hover/click incluidos.

flex solo versión

.row {
  width: 60%;
  margin: auto;
  display: flex;
}
.button {
  flex: 0 0 33.3%;
  text-align: center;
  position: relative;
  padding: 10px 5px;
  box-sizing: border-box;
  pointer-events: none;
}
.button#number0 {
  flex: 0 0 66.6%;
}

.button:before,
.button:after {
  content: " ";
  border-radius: 5px;
  border: 1px solid gray;  
  cursor: pointer;
  position: absolute;
  left: 5px;
  top: 5px;
  right: 5px;
  bottom: 5px;
  pointer-events: auto;
}
.button:before {
  background: rgba(255, 255, 255, 0.9);
  z-index: -1
}
.button:hover:before {
  background: rgba(0, 0, 0, 0.1);
}
.button:hover:after {
  border: 2px solid red;
}
.button:active:before {
  background: rgba(255, 0, 0, 0.5);
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

flex versión con una alternativa display: table para navegadores que no soporta el nuevo flexbox modelo.

.row {
  display: table;              /* remove for flex only */
  width: 60%;
  margin: auto;
  display: flex;
}
.button {
  display:table-cell;          /* remove for flex only */
  width: 33.3%;                /* remove for flex only */
  flex: 0 0 33.3%;
  text-align: center;
  position: relative;
  padding: 10px 5px;
  box-sizing: border-box;
  pointer-events: none;
}
.button#number0 {
  width: 66.6%;                /* remove for flex only */
  flex: 0 0 66.6%;
}

.button:before,
.button:after {
  content: " ";
  border-radius: 5px;
  border: 1px solid gray;  
  cursor: pointer;
  position: absolute;
  left: 5px;
  top: 5px;
  right: 5px;
  bottom: 5px;
  pointer-events: auto;
}
.button:before {
  background: rgba(255, 255, 255, 0.9);
  z-index: -1
}
.button:hover:before {
  background: rgba(0, 0, 0, 0.1);
}
.button:hover:after {
  border: 2px solid red;
}
.button:active:before {
  background: rgba(255, 0, 0, 0.5);
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

Actualizar 2:

Además de la respuesta de Michael_B, que por cierto tiene una muy buena explicación, está aquí una versión actualizada, que en realidad da la alineación deseada sin el, en este caso, 1-2 px apagado.

Aquí hay una muestra fiddle , y una imagen , de las versiones mía y Michael_B, donde el borde se ha aumentado un poco para que sea más fácil ver el desalineación.

Todo se reduce a cómo flexbox calcula los tamaños cuando border/padding está presente, que puede leer más en este post, donde box-sizing: border-box necesita ser configurado junto con algunos ajustes más, que se comenta en el código.

Aquí está mi violín y fragmento

.row {
    display: flex;
    width: calc(100% - 30px);   /* 30px = the sum of the buttons margin: 5px
                                          to avoid horizontal scroll            */
}

.button {
    display: flex;
    flex-basis: 33.33%;
    flex-shrink: 0;             /* we need flex-grow/shrink to be 1/0 to make
                                   it calculate the size properly               */
    box-sizing: border-box;     /* to take out the borders when calculate the 
                                   flex shrink/grow factor                      */
    justify-content: center;
    align-items: center;
    margin: 5px;
    border-radius: 5px;
    border: 1px solid gray;
    background: rgba(0, 0, 0, 0.1);
    cursor: pointer;
}

#number0 {
    flex-basis: calc(66.66% + 10px);
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1">1</div>
        <div class="button number" id="number2">2</div>
        <div class="button number" id="number3">3</div>
    </div>
    <div class="row">
        <div class="button number" id="number4">4</div>
        <div class="button number" id="number5">5</div>
        <div class="button number" id="number6">6</div>
    </div>
    <div class="row">
        <div class="button number" id="number7">7</div>
        <div class="button number" id="number8">8</div>
        <div class="button number" id="number9">9</div>
    </div>
    <div class="row">
        <div class="button number" id="number0">0</div>
        <div class="button" id="colon">:</div>
    </div>
</div>

Actualizar:

flex solo versión, con un cambio menor de la estructura html existente, usando pseudo elementos.

.row {
  display: flex;
}
.button {
  flex: 0 0 33.3%;
}
.button:after {
  content: attr(data-nr);
  display: block;
  border-radius: 5px;
  border: 1px solid gray;
  background: rgba(255, 255, 255, 0.9);
  text-align: center;
  padding: 3px;
  margin: 5px;
  cursor: pointer;
}
.button#number0 {
  flex: 0 0 66.6%;
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1" data-nr="1"></div>
        <div class="button number" id="number2" data-nr="2"></div>
        <div class="button number" id="number3" data-nr="3"></div>
    </div>
    <div class="row">
        <div class="button number" id="number4" data-nr="4"></div>   
        <div class="button number" id="number5" data-nr="5"></div>
        <div class="button number" id="number6" data-nr="6"></div>
    </div>
    <div class="row">
        <div class="button number" id="number7" data-nr="7"></div>
        <div class="button number" id="number8" data-nr="8"></div>
        <div class="button number" id="number9" data-nr="9"></div>
    </div>
    <div class="row">
        <div class="button number" id="number0" data-nr="0"></div>
        <div class="button" id="colon" data-nr=":"></div>
    </div>
</div>

flex versión, con un cambio menor de la estructura html existente, usando pseudo elementos, y tiene una alternativa display: table para navegadores que no soporta el nuevo modelo flexbox (como IE8/9).

.row {
  display: table;
  width: 100%;
}
.button {
  display: table-cell;
  width: 33.3%;
  padding: 5px;
}
.button:after {
  content: attr(data-nr);
  display: block;
  border-radius: 5px;
  border: 1px solid gray;
  background: rgba(255, 255, 255, 0.9);
  text-align: center;
  padding: 3px;
  cursor: pointer;
}
.button#number0 {
  width: 66.6%;
}


@supports (display: flex) {
  .row {
    display: flex;
  }
  .button {
    display: block;
    width: auto;
    flex: 0 0 33.3%;
    padding: 0;
  }
  .button#number0 {
    flex: 0 0 66.6%;
  }
  .button:after {
    margin: 5px;
  }  
}
<div class="numbers">
    <div class="row">
        <div class="button number" id="number1" data-nr="1"></div>
        <div class="button number" id="number2" data-nr="2"></div>
        <div class="button number" id="number3" data-nr="3"></div>
    </div>
    <div class="row">
        <div class="button number" id="number4" data-nr="4"></div>   
        <div class="button number" id="number5" data-nr="5"></div>
        <div class="button number" id="number6" data-nr="6"></div>
    </div>
    <div class="row">
        <div class="button number" id="number7" data-nr="7"></div>
        <div class="button number" id="number8" data-nr="8"></div>
        <div class="button number" id="number9" data-nr="9"></div>
    </div>
    <div class="row">
        <div class="button number" id="number0" data-nr="0"></div>
        <div class="button" id="colon" data-nr=":"></div>
    </div>
</div>
 10
Author: LGSon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:56

Creo que todo lo que Michael_B dijo es correcto. Solo las soluciones son un poco incómodas. Personalmente no me gusta Calc. Simplemente no se siente bien.

El problema que tienes es más general. Pones demasiadas responsabilidades en un elemento. En este caso es la clase .button. Flex y margen con flex-grow es demasiada responsabilidad. Trata de romper eso. Significa más elementos DOM, pero te ahorra mucho dolor.

.numbers {
  display: flex;
  flex-direction: column;
  max-width: 200px;
  margin: 0 auto;
}

.row {
  display: flex;
  flex-direction: row;
  flex-grow: 1;
  justify-content: space-between;
}

.row > .box {
  display: flex;
  flex-basis: 33.3333%;
  justify-content: center;
  align-items: center;
}

.row > .box.box-2 {
  flex-basis: 66.6667%;
}

.button {
  border-radius: 5px;
  border: 1px solid gray;
  background: rgba(255, 255, 255, 0.2);
  cursor: pointer;
  width: auto;
  text-align: center;
  margin: 5px;
  width: 100%;
}
<div class="numbers">
    <div class="row">
      <div class="box"><div class="button number" id="number1">1</div></div>
      <div class="box"><div class="button number" id="number2">2</div></div>
      <div class="box"><div class="button number" id="number3">3</div></div>
    </div>
    <div class="row">
      <div class="box"><div class="button number" id="number4">4</div></div>
      <div class="box"><div class="button number" id="number5">5</div></div>
      <div class="box"><div class="button number" id="number6">6</div></div>
    </div>
    <div class="row">
      <div class="box"><div class="button number" id="number7">7</div></div>
      <div class="box"><div class="button number" id="number8">8</div></div>
      <div class="box"><div class="button number" id="number9">9</div></div>
    </div>
    <div class="row">
      <div class="box box-2"><div class="button number" id="number0">0</div></div>
      <div class="box"><div class="button" id="colon">:</div></div>
        
        
    </div>
</div>
 7
Author: HerrSerker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-13 11:16:31

Flexbox no responde bien a los márgenes, en mi opinión.

El mejor enfoque / solución que prefiero es asegurarme de que todos los hijos flex tengan márgenes 0, establecer el contenedor flex en justify-content: space-between; y luego dar a los hijos un ancho total inferior al 100%. El resto será su margen.

En otras palabras, si desea dos elementos por fila, establezca cada uno en un 49% de ancho y tendrá un 2% de espacio entre ellos. Tres elementos, cada uno con un 32% de ancho y tendrás un 2% entre ellos. En la calculadora por ejemplo, la celda 0 debe tener un 66% de ancho y el resto un 32%.

Editar: Tenga en cuenta que debido a las razones (es decir, que content-box es terrible), si alguno de sus hijos tienen fronteras, necesitará usar box-sizing: border-box para que mi sugerencia funcione correctamente.

Https://jsfiddle.net/r3L1mtbe/2 /

 4
Author: Lee Saxon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-22 05:44:01

Es importante utilizar la abreviatura para dar cabida a la compatibilidad entre navegadores:

.row {
    display: flex;
    flex-direction: row; /* <-- this is the default so unnecessary to state */
    flex-grow: 1;
    justify-content: space-between;
}

.button {
    display: flex;
   /* flex-grow: 1; replace with shorthand */ 
   flex:1 0 100%; /* probably making the "width: 100%;" unnecessary */
    justify-content: center;
}

.button#number0 {
    /* flex-grow: 2; replace with shorthand */ 
   flex:2 0 100%;
}

.button#colon {
    /* flex-grow: 1; replace with shorthand */ 
    flex:1 0 100%;
}
 1
Author: Carol McKay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-21 07:10:01