Chrome / Safari no llena el 100% de la altura del padre flexible

235

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 .containercontiene 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 la display: table-celltécnica. Pero el uso de pantallas de mesa requiere el uso de una altura del 100%.

Mi problema es que .item-inner { height: 100% }no funciona en webkit (Chrome).

  1. ¿Hay alguna solución para este problema?
  2. ¿O hay una técnica diferente para hacer que todos .itemllenen la altura del padre con texto vertical alineado 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>

Ricardo Castañeda
fuente
1
ahora se ha solucionado - bugs.chromium.org/p/chromium/issues/detail?id=426898
vsync
3
Esto no es específicamente relevante para OP, pero podría ser relevante para los googlers que aterrizan aquí para "la altura absoluta de la pantalla de safari 100% no funciona" o algo similar. Tengo un elemento como este dentro de un contenedor flexible y tuve que especificar top:0y left:0hacer que aparezca como se esperaba.
AlexMA
1
@vsync aún no se ha solucionado: stackoverflow.com/questions/46226298/…
TylerH

Respuestas:

408

Solución

Use contenedores flexibles anidados.

Deshágase de las alturas porcentuales. Deshágase de las propiedades de la tabla. Deshacerse de vertical-align. Evitar el posicionamiento absoluto. Solo quédate con flexbox hasta el final.

Aplique display: flexal elemento flexible ( .item), convirtiéndolo en un contenedor flexible. Esto se establece automáticamente align-items: stretch, lo 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 (p height: 100%. Ej. ), Ignorará la align-items: stretchllegada del padre. Para que el stretchvalor predeterminado funcione, la altura del niño debe calcular auto (explicación completa ).

Pruebe esto (sin cambios en HTML):

jsFiddle demo


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 heightpropiedad

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 en auto.

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

El contenedor de tercer nivel tiene una altura definida: .item-inner { height: 100%; }

Pero entre ellos, el contenedor de segundo nivel .item- no tiene una altura definida. Webkit lo ve como un eslabón perdido.

.item-innerle está diciendo a Chrome: dameheight: 100% . Chrome mira al padre ( .item) como referencia y responde: ¿ 100% de qué? No veo nada (ignorando la flex: 1regla que está allí). Como resultado, se 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-growcomo referencia principal adecuada si se usa junto conflex-basis (cualquier valor numérico funciona ( autono lo hará), incluido flex-basis: 0). Al escribir estas líneas, sin embargo, esta solución falla en Safari.


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 la falta de enlaces, que los navegadores basados ​​en Webkit consideran una violación de la especificación.

Tenga en cuenta que min-heighty max-heightno son aceptables. Debe ser la heightpropiedad.

Más detalles aquí: trabajar con la heightpropiedad CSS y los valores de porcentaje

2. Posicionamiento relativo y absoluto de CSS

Aplicar position: relativea los padres y position: absoluteal niño.

Tamaño del niño con height: 100%y width: 100%, o utilizar 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 button? ¿Por qué no eliminar .itemo .item-inner, o ambos? Aunque los buttonelementos a veces fallan como contenedores flexibles , pueden ser elementos flexibles. Considere crear buttonun hijo de .containero .itemy eliminar el marcado gratuito.

Aquí hay un ejemplo:

4. Contenedores flexibles anidados (recomendado)

Deshágase de las alturas porcentuales. Deshágase de las propiedades de la tabla. Deshacerse de vertical-align. Evitar el posicionamiento absoluto. Solo quédate con flexbox hasta el final.

Aplique display: flexal elemento flexible ( .item), convirtiéndolo en un contenedor flexible. Esto se establece automáticamente align-items: stretch, lo 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 (p height: 100%. Ej. ), Ignorará la align-items: stretchllegada del padre. Para que el stretchvalor predeterminado funcione, la altura del niño debe calcular auto (explicación completa ).

Pruebe esto (sin cambios en HTML):

jsFiddle

Michael Benjamin
fuente
3
Solucioné
solo un comentario tomado del enlace de explicación de esta gran publicación: "alinear elementos: estiramiento" hace que los niños flexibles se expandan a través del eje transversal del contenedor, es decir, estirar a los niños a la altura, el contenedor flexible debe tener dirección flexible como fila
littlewhywhat
3
Puedo confirmar que el uso de contenedores flexibles anidados funciona mejor. Usamos flexbox de manera inconsistente en nuestro menú. En lugar de usar flex: 1 y display: flex en contenedores primarios, usamos height: 100% en algunos lugares. Todo funcionó como se esperaba después de reemplazar las definiciones correspondientes. ¡Gracias @Michael_B por la pista! :)
flocbit
1
@JamesHarrington, CSS ha crecido con el tiempo y es complejo ;-)
DerMike
Cada solución debe ser una respuesta diferente. ¿Cómo puedo votar en una solución específica de esta manera?
Jp_
14

Solución: eliminar height: 100%en .item-inner y agregar display: flexen .item

Demostración: https://codepen.io/tronghiep92/pen/NvzVoo

nghiepit
fuente
Gracias, esto me ayudó. ¿Los niños flexibles se estiran automáticamente a la altura de su contenedor? ¿Es por esto que esto funciona?
Reece Kenney el
@ReeceKenney, sí, a tus preguntas. Esto se explica en mi respuesta. Ver solución # 4.
Michael Benjamin
10

Especificar un atributo flex para el contenedor funcionó para mí:

.container {
    flex: 0 0 auto;
}

Esto asegura que la altura esté establecida y que tampoco crezca.

huseyin39
fuente
6

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 está utilizando la align-items: stretch;propiedad para el elemento primario, elimine la height : 100%del elemento secundario.

Narasinghe
fuente
2

He tenido 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%;
}
Diseñador023
fuente
0

Tuve un error similar, pero mientras usaba un número fijo para la altura y no un porcentaje. También era un contenedor flexible dentro del cuerpo (que no tiene una altura especificada). Parecía que en Safari, mi contenedor flexible tenía una altura de 9px por alguna razón, pero en todos los demás navegadores mostraba la altura correcta de 100px especificada en la hoja de estilo.

Logré que funcionara agregando las propiedades heighty min-heighta la clase CSS.

Lo siguiente funcionó para mí en Safari 13.0.4 y Chrome 79.0.3945.130:

.flex-container {
  display: flex;
  flex-direction: column;
  min-height: 100px;
  height: 100px;
}

¡Espero que esto ayude!

Carlton Lindsay
fuente