css - toda - height de una pagina
Chrome/Safari no llena el 100% de la altura del padre flexible (4)
Quiero tener un menú vertical con una altura específica.
Cada niño debe llenar la altura del padre y tener texto alineado en el medio.
El número de hijos es aleatorio, así que tengo que trabajar con valores dinámicos.
Div
.container
contiene un número aleatorio de hijos (
.item
) que siempre tienen que llenar la altura del padre.
Para lograr eso usé flexbox.
Para hacer enlaces con texto alineado al medio, estoy usando
display: table-cell
técnica
display: table-cell
.
Pero usar pantallas de mesa requiere usar una altura del 100%.
Mi problema es que
.item-inner { height: 100% }
no funciona en webkit (Chrome).
- ¿Hay alguna solución para este problema?
-
¿O hay una técnica diferente para hacer que todos los
.item
llenen la altura del padre con el texto alineado verticalmente al medio?
Ejemplo aquí jsFiddle, debe verse en Firefox y Chrome
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
.item {
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
height: 100%;
width: 100%;
display: table;
}
a {
background: orange;
display: table-cell;
vertical-align: middle;
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
Solución
Use contenedores flexibles anidados.
Deshágase de las alturas porcentuales.
Deshágase de las propiedades de la tabla.
Deshágase de
vertical-align
.
Evitar el posicionamiento absoluto.
Solo quédate con flexbox hasta el final.
Aplique
display: flex
al elemento flex (
.item
), convirtiéndolo en un contenedor flexible.
Esto establece automáticamente
align-items: stretch
, que le dice al niño (
.item-inner
) que expanda la altura completa del padre.
Importante: elimine las alturas especificadas de los elementos flexibles para que este método funcione.
Si un niño tiene una altura especificada (por ejemplo,
height: 100%
), ignorará los elementos de
align-items: stretch
proveniente del padre.
Para que el
stretch
predeterminado funcione, la altura del niño debe calcularse en
auto
(explicación completa
).
Pruebe esto (sin cambios en HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
Explicación
Mi problema es que
.item-inner { height: 100% }
no funciona en webkit (Chrome).
No funciona porque está utilizando el porcentaje de altura de una manera que no se ajusta a la implementación tradicional de la especificación.
10.5 Altura del contenido: la propiedad de
height
porcentaje
Especifica un porcentaje de altura. El porcentaje se calcula con respecto a la altura del bloque que contiene la caja generada. Si la altura del bloque contenedor no se especifica explícitamente y este elemento no está en una posición absoluta, el valor se calcula comoauto
.auto
La altura depende de los valores de otras propiedades.
En otras palabras, para que la altura porcentual funcione en un elemento secundario en flujo, el elemento primario debe tener una altura establecida.
En su código, el contenedor de nivel superior tiene una altura definida:
.container { height: 20em; }
.container { height: 20em; }
El contenedor de tercer nivel tiene una altura definida:
.item-inner { height: 100%; }
.item-inner { height: 100%; }
Pero entre ellos, el contenedor de segundo nivel -
.item
-
no
tiene una altura definida.
Webkit ve eso como un eslabón perdido.
.item-inner
le dice a Chrome:
dame
height: 100%
.
Chrome mira al padre (
.item
) como referencia y responde: ¿
100% de qué?
No veo nada
(ignorando la regla
flex: 1
que está allí).
Como resultado, aplica
height: auto
(altura de contenido), de acuerdo con la especificación.
Firefox, por otro lado, ahora acepta la altura flexible de un padre como referencia para la altura porcentual del niño. IE11 y Edge también aceptan alturas flexibles.
Además, Chrome aceptará
flex-grow
como una referencia principal adecuada
si se usa junto con
flex-basis
(cualquier valor numérico funciona (
auto
no), incluida
flex-basis: 0
).
Al escribir estas líneas, sin embargo, esta solución falla en Safari.
#outer {
display: flex;
flex-direction: column;
height: 300px;
background-color: white;
border: 1px solid red;
}
#middle {
flex-grow: 1;
flex-basis: 1px;
background-color: yellow;
}
#inner {
height: 100%;
background-color: lightgreen;
}
<div id="outer">
<div id="middle">
<div id="inner">
INNER
</div>
</div>
</div>
Cuatro soluciones
1. Especifique una altura en todos los elementos principales
Una solución confiable para varios navegadores es especificar una altura en todos los elementos principales. Esto evita enlaces faltantes, que los navegadores basados en Webkit consideran una violación de la especificación.
Tenga en cuenta que
min-height
max-height
no son aceptables.
Debe ser la propiedad de
height
.
Más detalles aquí:
trabajar con la propiedad de
height
CSS y los valores de porcentaje
2. Posicionamiento relativo y absoluto de CSS
Aplicar
position: relative
al padre y
position: absolute
al hijo.
Tamaño del niño con
height: 100%
y
width: 100%
, o utilice las propiedades de desplazamiento:
top: 0
,
right: 0
,
bottom: 0
,
left: 0
.
Con posicionamiento absoluto, la altura porcentual funciona sin una altura especificada en el padre.
3. Eliminar contenedores HTML innecesarios (recomendado)
¿Hay necesidad de dos contenedores alrededor del
button
?
¿Por qué no eliminar
.item
o
.item-inner
, o ambos?
Aunque los
elementos de
button
veces fallan como contenedores flexibles
, pueden ser elementos flexibles.
Considere convertir el
button
un elemento secundario de
.container
o
.item
, y eliminar el marcado gratuito.
Aquí hay un ejemplo:
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
a {
flex: 1;
background: orange;
border-bottom: 1px solid white;
display: flex; /* nested flex container (for aligning text) */
align-items: center; /* center text vertically */
justify-content: center; /* center text horizontally */
}
<div class="container">
<a>Button</a>
<a>Button</a>
<a>Button</a>
</div>
4. Contenedores flexibles anidados (recomendado)
Deshágase de las alturas porcentuales.
Deshágase de las propiedades de la tabla.
Deshágase de
vertical-align
.
Evitar el posicionamiento absoluto.
Solo quédate con flexbox hasta el final.
Aplique
display: flex
al elemento flex (
.item
), convirtiéndolo en un contenedor flexible.
Esto establece automáticamente
align-items: stretch
, que le dice al niño (
.item-inner
) que expanda la altura completa del padre.
Importante: elimine las alturas especificadas de los elementos flexibles para que este método funcione.
Si un niño tiene una altura especificada (por ejemplo,
height: 100%
), ignorará los elementos de
align-items: stretch
proveniente del padre.
Para que el
stretch
predeterminado funcione, la altura del niño debe calcularse en
auto
(explicación completa
).
Pruebe esto (sin cambios en HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle
Para Safari móvil Hay una solución de navegador. necesita agregar -webkit-box para dispositivos iOS.
Ex.
display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;
si usa elementos de
align-items: stretch;
propiedad del elemento primario, elimine la
height : 100%
del elemento secundario.
Solución: eliminar la
height: 100%
en
.item-inner
y agregar
display: flex
en
.item
Demostración: https://codepen.io/tronghiep92/pen/NvzVoo
Tuve un problema similar en iOS 8, 9 y 10 y la información anterior no pudo solucionarlo, sin embargo, descubrí una solución después de un día de trabajar en esto. De acuerdo, no funcionará para todos, pero en mi caso mis artículos se apilaron en una columna y tenían una altura de 0 cuando debería haber sido altura de contenido. Cambiar el CSS para que sea fila y ajuste solucionó el problema. Esto solo funciona si tiene un solo elemento y están apilados, pero como me llevó un día descubrirlo, ¡pensé que debería compartir mi solución!
.wrapper {
flex-direction: column; // <-- Remove this line
flex-direction: row; // <-- replace it with
flex-wrap: wrap; // <-- Add wrapping
}
.item {
width: 100%;
}