html - end - flex-grow no dimensiona los elementos flex como se esperaba
flex-shrink (5)
Parece que el contenido dentro de un div flexible afecta su tamaño calculado con respecto a la propiedad
flex-grow
.
¿Estoy haciendo algo mal?
En el violín que se proporciona 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'' como el ancho de 2 números, por lo tanto,
flex-grow: 2
y el '':'' (dos puntos) debe ser del tamaño de 1 número, por lo tanto,
flex-grow: 1
.
¿Me estoy perdiendo de algo?
El lado derecho del ''0'' debe estar alineado con el 8, 5 y 2 sobre él. Está un poco apagado.
.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>
Análisis corto
El problema es que las filas 1-3 tienen dos márgenes horizontales y la fila 4 solo tiene uno.
Con márgenes horizontales de 10 px cada uno, la fila 4 tiene 10 px más espacio libre que las otras filas. Esto elimina la alineación de las columnas.
Debido
flex-grow
aplica solo al espacio libre y está fuertemente influenciado por el contenido y los márgenes, no es la forma más segura de dimensionar elementos flexibles.
Prueba
flex-basis
lugar.
Agregue esto a su código:
.button { flex-basis: 33.33%; }
#number0 { flex-basis: calc(66.67% + 10px); }
* { box-sizing: border-box; }
.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); }
* { box-sizing: border-box; }
<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>
Análisis extendido
Tu escribiste:
Parece que el contenido dentro de un div flexible afecta su tamaño calculado con respecto a la propiedad
flex-grow
¿Estoy haciendo algo mal?
La fuente de su problema no es el contenido dentro del elemento flexible.
Tu escribiste:
En el violín que se proporciona 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
. ¿Me estoy perdiendo de algo?
Si.
Su interpretación de la propiedad
flex-grow
es incorrecta.
flex-grow
no está destinado a definir el tamaño de un elemento flex.
Su trabajo es distribuir espacio libre en el contenedor flexible entre los artículos.
Al aplicar
flex-grow: 1
a un grupo de elementos flex, les está diciendo que distribuyan el espacio libre de manera uniforme entre ellos.
Por eso, en su demostración, las filas 1, 2 y 3 tienen elementos flexibles de igual tamaño.
Cuando aplica
flex-grow: 2
, le está diciendo al elemento flex que consuma el doble de espacio libre que los elementos con
flex-grow: 1
.
Pero, ¿dónde influye el segundo margen de 10 píxeles de las filas anteriores en el diseño de la fila 4?
La razón por la cual la alineación está desactivada en la fila 4 es que la fila 4 tiene un margen menos que las otras filas, lo que significa que la fila 4 tiene 10px más espacio libre que las otras filas.
Notarás que si eliminas la regla de margen obtienes 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é sucede en la fila cuatro a ese segundo margen de 10 píxeles?
Se absorbe por los dos elementos flexibles.
Así es como
flex-grow
distribuye el espacio extra en la fila cuatro:
- El elemento flexible restante (con contenido "0") tiene
flex-grow: 2
. (.button#number0
en su código.)- El elemento de la derecha (con el contenido ":") tiene
flex-grow: 1
. (.button#colon
en su código).- El segundo margen entre elementos, que aparece solo en filas con tres elementos flexibles, tiene 10 píxeles de ancho. (El código dice 5px alrededor de cada elemento, pero en CSS los márgenes horizontales nunca colapsan . Además, en flexbox, no se colapsa ningún margen ).
- La suma total de los valores de
flex-grow
es tres. Entonces, dividamos 10 px por 3. Ahora sabemos que la proporción de 1 es 3.33 px.- Por lo tanto, el elemento flexible a la izquierda obtiene 6.66px del espacio extra, y el elemento flexible a la derecha obtiene 3.33px.
- Digamos que el elemento flexible restante tenía
flex-grow: 3
lugar. Luego, el elemento flexible a la izquierda obtendría 7.5px, y el elemento flexible a la derecha obtendría 2.5px.
La última parte de tu pregunta dice:
El lado derecho del ''0'' debe estar alineado con el 8, 5 y 2 sobre él. Está un poco apagado.
Debido
flex-grow
aplica solo al espacio libre y está fuertemente influenciado por el contenido y los márgenes, no es la forma más segura de dimensionar elementos flexibles.
Prueba
flex-basis
lugar.
Agregue esto a su código:
.button { flex-basis: 33.33%; }
#number0 { flex-basis: calc(66.67% + 10px); }
* { box-sizing: border-box; }
.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); }
* { box-sizing: border-box; }
<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>
Referencias
-
definición
flex-grow
~ MDN -
definición de
flex-basis
~ MDN - 7.2 Componentes de flexibilidad ~ W3C
EXTRA: SOLUCIÓN GRID CSS
Con el advenimiento de CSS Grid, el código para todo este diseño se puede simplificar enormemente.
.numbers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(26%, 1fr));
grid-gap: 10px;
}
#number0 {
grid-column: span 2;
}
/* non-essential decorative styles */
.button {
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
border: 1px solid gray;
}
<div class="numbers">
<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 class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
<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 class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
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 and Margin con flex-grow es demasiada responsabilidad.
Intenta romper eso aparte.
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>
Es importante utilizar la taquigrafía para acomodar la compatibilidad con varios 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%;
}
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 niños flexibles tengan márgenes 0, configure el contenedor flexible para
justify-content: space-between;
, y luego dé a los niños un ancho total inferior al 100%.
El resto será su margen.
En otras palabras, si desea dos elementos por fila, defina cada uno al 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 el ejemplo de la calculadora, la celda
0
debe tener un 66% de ancho y el resto un 32%.
Editar: Tenga en cuenta que debido a razones (es decir, que
content-box
es terrible), si alguno de sus hijos tiene bordes, deberá usar
box-sizing: border-box
para que mi sugerencia funcione correctamente.
Actualización 3:
Descubrí otra forma 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, incluidos los efectos de desplazamiento / clic del botón.
versión solo
flex
.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>
versión
flex
con
display: table
respaldo para navegadores que no admite el nuevo modelo flexbox.
.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>
Actualización 2:
Además de la respuesta de Michael_B, que por cierto tiene una muy buena explicación, aquí hay una versión actualizada, que realmente proporciona la alineación deseada sin, en este caso, 1-2 px apagado.
Aquí hay una muestra de violín y una imagen de las versiones mías y Michael_B, donde el borde se ha aumentado un poco para que sea más fácil ver la desalineación.
Todo se reduce a cómo flexbox calcula los tamaños cuando está presente el
border
/
padding
, sobre el que puede
leer más en esta publicación,
donde se debe configurar el
box-sizing: border-box
junto con algunos ajustes más, que se comentan 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:
versión solo
flex
, con un cambio menor de la estructura html existente, utilizando 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>
versión
flex
, con un cambio menor de la estructura html existente, que utiliza pseudo elementos, y tiene una
display: table
respaldo de
display: table
para navegadores que no admite 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>