Hijos con height en porcentaje cuando el padre tiene declarado min-height y no height

Traducción del artículo de Roger Johansson sobre las discrepancias en el cálculo de alturas en los hijos con height en porcentaje cuando el padre tiene declarado min-height y no height

Hijos con height en porcentaje cuando el padre tiene declarado min-height y no height

✎ 8
COLABORACIÓN AUTOR INVITADO

Hijos con height en porcentaje cuando el padre tiene declarado min-height y no heightCentrando la situación: tengamos un elemento al que se declara altura mínima min-height en px, pero por ser desconocido y variable a priori el contenido alojado en él (su hijo) no podemos fijar la altura height del padre.

Y la segunda parte del planteamiento, necesitamos que el hijo ocupe toda la altura de su padre. Así que anotamos en su regla height: 100%;

Lo que normalmente esperamos es que el hijo ocupe toda la altura del padre. Pero resulta que no ocurre eso. El resultado, poco intuitivo, es lo que dicen las especificaciones de CSS 2.1.

Nota sobre la autoría: tanto la introducción como el resto del artículo es obra original de Roger Johansson publicado en la página 456 Berea St. Al pie de este artículo tienes todos los datos al respecto. Por favor, pasa por el original para acceder al original libre de errores.

Dice la especificación

El porcentaje se calcula sobre la altura generada de la caja contenedora. Si la altura del bloque de contención no se especifica explícitamente (por ejemplo, depende de la altura de su contenido) y el elemento no está posicionado de forma absoluta, el valor computado es auto.

Entiendo que "la caja contenedora" es el elemento padre. En el supuesto, este padre no tiene un height explícito. Pero sí tiene un min-height. Así que su altura sólo depende en parte de su contenido.

y ¿qué hay sobre el min-height?. Sobre ella dice la especificación cuando se usan unidades de longitud:

Especifica una altura (min o máx) computada fija.

Parece que esto debería ser tenido en cuenta para el cálculo de la altura de los hijos. Pero los navegadores no lo hacen. Por lo que al parecer no estoy leyendo la especificación correcta.

Lo que los navegadores hacen

Todos los navegadores que he testado ignoran height: 100% en el elemento hijo y su altura es la altura de su contenido. Aunque no es lo que yo quería, por lo menos es cross-browser.

Mientras jugueteaba con este problema di al elemento principal una altura de 1 píxel. Y ahora las cosas se ponen interesantes con diferencias entre los navegadores.

Esto probablemente sería más fácil de entender si todos los navegadores se comportaban de la misma manera. Pero no lo hacen.

Los siguientes navegadores no fijan la altura del elemento secundario (especificado en porcentaje) en función de la altura calculada de su padre, si sólo se utiliza min-height:

  • Safari 6.0.5
  • Safari en iOS 6.1
  • Opera 12.15
  • IE 8

Pero estos navegadores ampliarán la altura porcentual del hijo al min-height de su padre si el padre también tiene una altura especificada, incluso si es sólo un píxel:

  • Firefox 21
  • WebKit Nightly (verified with r151959)
  • Chrome 27
  • IE 7, 9, 10

Echa un vistazo a la altura en % cuando el padre tiene min-height y no height en esta página de demostración para ver la diferencia.

Una vez que Webkit se actualice y Opera cambie a Webkit me imagino que todos los navegadores se comportan como el segundo grupo, ya que la última "nightly build" de Webkit lo hace.

Otra cosa interesante que sucede cuando el elemento padre tiene en la altura y min-height especificada la misma unidad: si su contenido hace al elemento más alto que su min-height, el contenido se desborda como si el min-height se conviertiera mágicamente en height. La altura computada del hijo se mantendrá en el valor del min-height especificada del padre. Por lo que se puedo decir que todos los navegadores se comportan igual en este sentido, por lo que lamentablemente no se puede simplemente añadir min-height: 1px al elemento padre y resolver el problema.

Lo que los desarrolladores esperan

En cuanto a mi, intuitívamente esperaba que el valor usado para calcular la altura en % del hijo fuese la altura computada del padre.Si el padre no tiene una altura explícita pero sí una mínima ese valor se debería utilizar para la altura calculada {dependiendo del modelo de caja (box-sizing) utilizado, los rellenos y bordes también pueden ser relevantes}.

No hay solución o truco mágico que ofrecer, sólo una observación del comportamiento del navegador que no me esperaba.

Créditos y autoría del artículo

456 Berea St

Lo que acabas de leer es una "traslación" al español (con errores y disculpas por mis carencias) del artículo original: Height in percent when parent has min-height and no height de @ Roger Johansson
Sitio original de publicación: 456 Berea St.
Si lo ves en mi blog es por la autorización otorgada por su autor:
YO: I want to translate one of your articles and publish it on my site. Is that ok?
AUTOR: Yes, absolutely, provided that you clearly state that it is a translation of one of my articles and link back to the original article, and that you don’t publish it on a spammy or off-topic site.

Comentarios: 8

  1. Muy interesantes estos experimentos. Como bien dice el autor primero (y supongo que el traductor adhiere) habrá que esperar a que se implemente de alguna forma en todos los navegadores. Sabemos que las interpretaciones que suelen hacer sobre las especificaciones pueden llegar a ser muy libres, aunque después de varios reclamos lo corrijan.

    No estoy aportando gran cosa, pero ¿probaste esto?

    <!DOCTYPE html>
    <html lang="es-ar">
    <body>

    <div style="height: 1px;
    min-height: 300px;
    background-color: silver;
    background-image: url(http://img84.imageshack.us/img84/6059/sombrapuzzle.gif); ">

    <div style="min-height: 100%;
    background-color: rgba(0, 0, 250, .5);
    color: maroon;
    font-size: 40px;
    line-height: 80px;
    font-weight: 900;
    text-align: center; "> KSESOCSS? </div>

    </div>

    </body>
    </html>


    La imagen está nada más que para confirmar que el hijo cubre toda la altura del padre, se la puede ver a través del fondo semitransparente.

    ResponderEliminar
    Respuestas
    1. En un primer vistazo rápido a tu ejemplo, Furoya, ie8 sólo crece lo que su contenido y FF, Chr, ie7 y 9 cubre toda la altura del padre.

      Aunque quizás no era eso en lo que tenía que fijarme.

      Un saludo

      Eliminar
    2. Sí, el punto era Opera que no andaba con 'height', pero con 'min-height' en el hijo lo estira hasta la altura del padre.
      Igual estamos en la misma, porque si hacemos crecer el contenido, éste se pasa del contenedor. El alto mínimo del padre sigue funcionando como un simple 'height'; y esto me pasa en todos, claro.
      Habrá que ver si se nos ocurre otra cosa.

      Eliminar
    3. A riesgo de parecer pesado, vuelvo a este asunto.
      Y antes de que alguien nos "recuerde" que esto es muy fácil de hacer con javascript, aclaro que hasta donde tengo entendido el problema es que el CSS no se comporta justamente como esperamos, y que la "solución" debería ser con CSS. Aunque más no sea por el reto.

      Vamos a afinar un poco el lápiz. Ya está mostrado cómo se consigue que un 'hijo' con altura en porcentaje (para el caso, 100%) se ajuste a su 'padre' (con una altura absoluta mínima declarada) aún cuando su contenido no le alcance para llegar a esa medida del ancestro. Pero resulta que si el contenido del 'hijo' lo obliga a crecer más que la altura de su 'padre', éste solamente llega hasta su medida mínima declarada, y su 'hijo' se desborda. Así que la 'altura mínima' se comporta como una 'altura' común y silvestre.

      Como estaba aún experimentando con el Opera, lo resolví así

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
      "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
      <html>
      <body>
      <form>
      <input type=button value="+" title="Agrega texto al 'hijo'."
      onclick="document.getElementById('hijo').innerHTML += '<br> KSESOCSS?'">
      &nbsp;
      <input type=button value="-" title="Quita texto al 'hijo'."
      onclick="var charly = document.getElementById('hijo').innerHTML.split('<br>');
      charly.pop();
      document.getElementById('hijo').innerHTML = charly.join('<br>'); ">
      Para Opera.
      </form>

      <div style="
      position: relative;
      min-height: 300px;
      /*height: 1px; */
      box-sizing: border-box;
      -moz-box-sizing: border-box;
      background-color: silver;
      border: 1px solid black;
      overflow: visible;
      display: block;
      width: 100%;
      " id=padre>

      <div style="
      position: absolute;
      left: -8px;
      height: 300px;
      width: 5px;
      background-color: maroon;
      cursor: help;
      /*border-bottom: solid 30px green; */
      " title="Marca de referencia, 300px de altura.">
      </div>

      <div style="
      min-height: 100%;
      background-image: url(http://img84.imageshack.us/img84/6059/sombrapuzzle.gif);
      color: maroon;
      font-size: 40px;
      line-height: 80px;
      font-weight: 900;
      text-align: center;
      overflow: visible;
      display: block;
      float: left;
      width: 100%;
      " id=hijo><br> KSESOCSS?<br> KSESOCSS?<br> KSESOCSS?</div>

      <div style="clear: both; overflow: hidden; height: 1px; background: blue; "></div>
      </div>

      </body>
      </html>


      El método es un viejo conocido de todos los diseñadores; pero aquí sólo anda en Opera :-P

      Eliminar
    4. En otros que probé los tamaños de 'padre' e 'hijo' no se igualan independientemente del contenido del 'hijo', hasta un mínimo regido por el 'padre'.

      Por supuesto que si a ambos les damos un min-height con el mismo valor absoluto en la práctica tiene que funcionar para cualquier navegador (que soporte min-height), pero el punto, insisto, es tener al vástago con altura relativa al progenitor.

      Ahora, si seguimos con casos prácticos, hacer que un 'hijo' con altura relativa al 'padre' se comporte como estamos queriendo cuando el 'padre' también tiene altura relativa, pero a su viewport, ... ya estamos de nuevo en problemas.
      Podemos darle a un contenedor un min-height: 45% del panel de documento, ¿pero qué porcentaje le damos al bloque contenido para igualarlo? Otro min-height: 45% no anda, porque lo va a tomar del alto de su 'padre', y nosotros queremos que tome su 100% que al fin es el 45% del 'abuelo'. Algo que ya probamos que los navegadores no hacen.
      Entonces en vez de "%" usamos otra unidad que nos salva la vida.

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
      "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
      <html>
      <body>
      <form>
      <input type=button value="+" title="Agrega texto al 'hijo'."
      onclick="document.getElementById('hijo').innerHTML += '<br> KSESOCSS?'">
      &nbsp;
      <input type=button value="-" title="Quita texto al 'hijo'."
      onclick="var charly = document.getElementById('hijo').innerHTML.split('<br>');
      charly.pop();
      document.getElementById('hijo').innerHTML = charly.join('<br>'); ">
      Para IE9?, FF19, Chr20.
      </form>

      <div style="
      position: relative;
      min-height: 45vh;
      /*height: 1px; */
      background-color: silver;
      border: 1px solid black;
      " id=padre>

      <div style="
      position: absolute;
      left: -8px;
      height: 45vh;
      width: 4px;
      background-color: maroon;
      cursor: help;
      /*border-bottom: solid 30px green; */
      " title="Marca de referencia, 45vh de altura (mitad +/- del viewport).">
      </div>

      <div style="
      min-height: 45vh;
      background-image: url(http://img84.imageshack.us/img84/6059/sombrapuzzle.gif);
      color: maroon;
      font-size: 40px;
      line-height: 1.6;
      font-weight: 900;
      text-align: center;
      text-decoration: underline overline;
      " id=hijo><br> KSESOCSS?<br> KSESOCSS?<br> KSESOCSS?</div>

      <div style="clear: both; overflow: hidden; height: 1px; background: blue; "></div>
      </div>

      </body>
      </html>


      El viewport's height (ó 1% del alto del bloque de documento) es una referencia fija para ambos, y se usa como un porcentaje.

      ¡Pero no funciona en Opera! ¦´0

      (Los ejemplos tienen unos botones para agregar o quitar texto con JS, pero no modifican el CSS. El comportamiento es el mismo que si se cambia el contenido tipeando el código fuente.
      El 'padre' tiene fondo gris, y el 'hijo' un diseño con transparencia para confirmar el ajuste de ambas capas.)

      Eliminar
    5. creo que al final nos estás ofreciendo una doble verificación de lo mismo que el autor original del artículo:
      Que hay un comportamiento dispar de los navegadores y
      Que no hay una "única solución" común y válida para todos.

      Un saludo y gracias por los aportes.

      Eliminar
    6. Por nada, es un gusto.

      El planteo original tiene una interpretación muy particular en los navegadores, y el parche del height: 1px ya vimos que tampoco es solución.
      Lo que intenté es llevar el problema a un caso práctico, donde importa el resultado más que el medio para alcanzarlo. Si necesitamos un bloque con altura a porcentaje del viewport, que contenga otro bloque a porcentaje del 'padre', pero que si el contenido de éste 'hijo' crece más de esa medida entonces que no se desborde sino que haga crecer a su 'padre' y su 'abuelo'; el último ejemplo funciona. Lo de Opera es temporal, hasta que reconozca las medidas vw, vh y demás. Para navegadores viejos ponemos un parche javascript (no sé si expression() andará todavía). Pero insisto: aunque se comporte como "%", la lógica de esas medidas es distinta. En este enlace hay una buena descripción del tópico.

      Y ya que estoy haciendo referencias, busqué un poco sobre otras posibles soluciones, y justo esta semana en un conocido foro en español estaban planteando un caso como éste para dar un "height 100% después de min-height", pero citaban el mismo artículo original de Roger Johansson. Que ya aclaró lo de "No solution or magic trick offered here, just an observation of browser behaviour that I wasn’t really expecting".

      Un saludo.

      Eliminar
    7. Fe de burratas : en el comentario anterior, donde dice
      "...que no se desborde sino que haga crecer a su 'padre' y su 'abuelo'..."
      debería decir
      "...que no se desborde sino que se haga crecer a sí mismo y a su 'padre'....

      El abuelo en este caso sería el viewport o panel del documento, que si redimensionamos arrastrando sus bordes podemos ver cómo el contenido se ajusta a su tamaño en porcentaje, hasta que se pega contra el tamaño del texto.
      Obviamente el documento no nos va a agrandar la pantalla. :-P

      Eliminar

EsCss RSS del Blog RSSS Comentarios Humans.txt ᛯ Diseno por Kseso SiteMap