CSS: configuración de ancho / alto como porcentaje menos píxeles

479

Estoy tratando de crear algunas clases CSS reutilizables para obtener más consistencia y menos desorden en mi sitio, y estoy atascado en tratar de estandarizar una cosa que uso con frecuencia.

Tengo un contenedor para el <div>que no quiero establecer la altura (porque variará dependiendo de en qué parte del sitio se encuentre), y dentro de él hay un encabezado <div>, y luego una lista desordenada de elementos, todo con CSS aplicado a ellos.

Se parece mucho a esto:

Widget

Quiero que la lista desordenada ocupe el espacio restante en el contenedor <div>, sabiendo que el encabezado <div>es 18pxalto. Simplemente no sé cómo especificar la altura de la lista como "el resultado de 100%menos 18px".

He visto esta pregunta en un par de contextos más sobre SO, pero pensé que valdría la pena volver a preguntar por mi caso particular. ¿Alguien tiene algún consejo en esta situación?

MegaMatt
fuente
66
Establecer un margen? __
kennytm
@KennyTM, supongo que estás sugiriendo que coloque un margen superior en mi lista desordenada de, digamos, 17px. Pero esto simplemente empuja la lista completa hacia abajo; no hace que se encoja para quedarse en el contenedor. Esencialmente, su altura actual se mantiene, pero solo 17px la empujó hacia abajo. Esto no resuelve mi problema, pero creo que es un paso en la dirección correcta porque he visto otros enfoques en línea que utilizan esta técnica.
MegaMatt
Acabo de resolver este problema en mi pregunta que publiqué aquí. stackoverflow.com/a/10420387/340947
Steven Lu
posible duplicado de CSS Cómo configurar la altura div 100% menos nPx
200_success

Respuestas:

895

Me doy cuenta de que esta es una publicación antigua, pero dado que no se ha sugerido, vale la pena mencionar que si está escribiendo para navegadores compatibles con CSS3, puede usar calc:

height: calc(100% - 18px);

Vale la pena señalar que no todos los navegadores actualmente admiten la función estándar CSS3 calc (), por lo que puede ser necesario implementar las versiones específicas de la función del navegador como las siguientes:

/* Firefox */
height: -moz-calc(100% - 18px);
/* WebKit */
height: -webkit-calc(100% - 18px);
/* Opera */
height: -o-calc(100% - 18px);
/* Standard */
height: calc(100% - 18px);
Levi Botelho
fuente
55
Sí, eso es normal por el momento.
Levi Botelho
1
Funciona para mí en IE10 y Chrome 27. ¡Señor, es mi héroe!
BrainSlugs83
3
@LeviBotelho Mi mal. No tenía espacio alrededor del operador.
usuario
20
Además, si usa Less, será mejor que compile un archivo con el lessc --strict-math=onfin de no evaluar la expresión dentro de calc- solo un problema que enfrenté y pasé mucho tiempo (a partir de Less 2.0.0)
Dmitry Wojciechowski
34
Tenga en cuenta que los espacios alrededor del signo menos no son opcionales: 100%-18px, 100%- 18pxy 100% -18pxno funcionó en los navegadores que he probado con.
nisetama
71

Para un enfoque un poco diferente, podría usar algo como esto en la lista:

position: absolute;
top: 18px;
bottom: 0px;
width: 100%;

Esto funciona siempre que el contenedor principal tenga position: relative;

Nicolas Galler
fuente
1
gracias hombre. el contenedor de los padres position: relative;me estaba haciendo tropezar.
gthmb
Para aquellos que pueden preguntarse como yo: la posición debe estar absoluteen el contenedor para niños, no puede ser relative.
Greg
Continuado (lo siento): Sin embargo, el padre solo necesita posicionamiento (también puede serlo absolute), y también debe configurarse a una altura del 100%.
Greg
1
Desafortunadamente, no funciona en Safari iOS 8. (Probado OK en el navegador de Android 4.1 y FF 34 estándar) :-(
Greg
Esto puede funcionar en algunos casos, pero también puede causar algunos problemas, ya que los elementos posicionados absolutamente se eliminan del flujo normal, consulte stackoverflow.com/a/12821537/4173303 .
Christophe Weis
26

Yo uso Jquery para esto

function setSizes() {
   var containerHeight = $("#listContainer").height();
   $("#myList").height(containerHeight - 18);
}

luego ato el cambio de tamaño de la ventana para recalcularlo cada vez que se cambia el tamaño de la ventana del navegador (si el tamaño del contenedor cambia con el cambio de tamaño de la ventana)

$(window).resize(function() { setSizes(); });
puffpio
fuente
12
@puffpio, ciertamente JS es una forma de lograr esto caso por caso. Pero como dije, estoy tratando de hacer que el CSS sea genérico, para que se aplique en varias páginas (por cierto, que heredan de una página maestra). Entonces preferiría no usar JS. Pero gracias por la respuesta.
MegaMatt
77
No pongas tu estilo dentro de JavaScript.
Greg
8
@greg, bueno, ayudaría si CSS tuviera algunas características más útiles. Algunas de las cosas por las que tienes que hacer hacks son ridículas.
Jonathan.
1
@puffpio Bonita solución de hecho. Pero los eventos vinculantes en la ventana no son una buena solución para mí. Si mi página tiene 3-4 o más de esos elementos, tengo que actualizar el alto y el ancho de cada elemento en el controlador de cambio de tamaño de la ventana. Esto sería un poco más lento que la solución CSS.
sachinjain024
1
"No pongas tu xyz dentro de JavaScript" es como decir "no soportas el 80% del mercado de navegadores".
Beejor
16

No defina la altura como un porcentaje, solo configure top=0y bottom=0, de esta manera:

#div {
   top: 0; bottom: 0;
   position: absolute;
   width: 100%;
}
kaleazy
fuente
¿Podría explicar por qué no establecer la altura en un porcentaje?
Shrikant Sharat
@ShrikantSharat Esto es parte de la especificación de formato visual para elementos posicionados absolutamente. Esta solución aprovecha la posibilidad 5., "'height' es 'auto', 'top' y 'bottom' no son 'auto', luego se establecen los valores 'auto' para 'margin-top' y 'margin-bottom' a 0 y resolver por 'altura' ". El navegador puede calcular la altura del elemento porque sabe qué tan lejos de la parte superior e inferior de su elemento primario debe estar.
Ross Allen el
Esto no parece funcionar, al menos en Firefox. Incluso con todos los elementos principales establecidos en height:100%y display:block. De lo contrario, sería una solución muy elegante. Curiosamente, margin:autotampoco puede centrar un objeto de ancho fijo verticalmente, a pesar de que funciona bien horizontalmente.
Beejor
8

Suponiendo una altura de encabezado de 17 px

Lista css:

height: 100%;
padding-top: 17px;

Encabezado css:

height: 17px;
float: left;
width: 100%;
ghoppe
fuente
Sí, a eso me estaba refiriendo.
dclowd9901
1
Eso no funcionó tan bien como esperaba. Mi lista desordenada se está iniciando debajo de ese encabezado div. El encabezado div está ocupando espacio en el contenedor, por lo que colocar el relleno superior de 17px en la lista solo lo empujará hacia abajo 17px desde donde ya está en la imagen de arriba, no 17px desde la parte superior del contenedor div. Aquí hay una foto de lo que obtengo con el CSS dado en la respuesta anterior: i41.tinypic.com/mcuk1x.jpg . Sin embargo, aprecio el esfuerzo, y si hay algo que crees que me estoy perdiendo, házmelo saber. Por cierto, tengo overflow: auto en la lista desordenada también.
MegaMatt
2
Sería mucho más fácil solucionar este problema si publicaras CSS real para que podamos ver exactamente qué interacciones están sucediendo. Otra cosa que puede probar es el margen superior negativo en su ul. ul { margin-top: -17px; }
ghoppe
7
  1. Use márgenes negativos en el elemento que le gustaría quitar menos píxeles. (elemento deseado)
  2. Hacer overflow:hidden;en el elemento contenedor
  3. Cambie a overflow:auto;en el elemento deseado.

¡Funcionó para mí!

desbest
fuente
3
Esto funcionó de maravilla para mí (ni siquiera necesité cambiar el desbordamiento). Gran solución, gracias!
Aaj
1
¡Completamente asombroso! Disfruté aprendiendo sobre la popular solución "CSS calc", pero esto es mucho más simple y tiene un soporte de navegador más amplio. ¡Márgenes negativos en perfecto!
Simon Steinberger
Esta es una solución muy elegante y compatible. El principal inconveniente es que el elemento debe ser una altura fija. De lo contrario, necesitaría usar JS o calc()centrarlo en tiempo de ejecución. Pero para muchos casos esto no debería ser un problema. Otra cosa a tener en cuenta es que el uso de muchos valores negativos para márgenes, relleno y compensaciones puede causar confusión al depurar el código más adelante, así que trate de simplificar las cosas.
Beejor
5

Prueba el tamaño de la caja. Para la lista:

height: 100%;
/* Presuming 10px header height */
padding-top: 10px;
/* Firefox */
-moz-box-sizing: border-box;
/* WebKit */
-webkit-box-sizing: border-box;
/* Standard */
box-sizing: border-box;

Para el encabezado:

position: absolute;
left: 0;
top: 0;
height: 10px;

Por supuesto, el contenedor principal debe tener algo como:

position: relative;
Ligero
fuente
3

Otra forma de lograr el mismo objetivo: cajas flexibles . Convierta el contenedor en una caja flexible de columna, y luego tendrá toda la libertad para permitir que algunos elementos tengan un tamaño fijo (comportamiento predeterminado) o para rellenar / reducir el espacio del contenedor (con flex-grow: 1 y flex- encogimiento: 1).

#wrap {        
  display:flex;
  flex-direction:column;
}
.extendOrShrink {
  flex-shrink:1;
  flex-grow:1;
  overflow:auto;
}

Consulte https://jsfiddle.net/2Lmodwxk/ (intente extender o reducir la ventana para notar el efecto)

Nota: también puede usar la propiedad abreviada:

   flex:1 1 auto;
tarulen
fuente
Creo que esta puede ser la mejor respuesta hoy porque significa no tener que especificar una altura establecida para el encabezado div (de 18px en mi pregunta original), y significa no tener que restar cosas como el relleno y el margen. No hay píxeles establecidos, y todo se cuida solo.
MegaMatt
2

Intenté algunas de las otras respuestas, y ninguna de ellas funcionó como yo quería. Nuestra situación era muy similar cuando teníamos un encabezado de ventana y la ventana era redimensionable con imágenes en el cuerpo de la ventana. Queríamos bloquear la relación de aspecto del cambio de tamaño sin necesidad de agregar cálculos para tener en cuenta el tamaño fijo del encabezado y aún así tener la imagen llena el cuerpo de la ventana.

A continuación, creé un fragmento muy simple que muestra lo que terminamos haciendo que parece funcionar bien para nuestra situación y debería ser compatible en la mayoría de los navegadores.

En nuestro elemento de ventana agregamos un margen de 20px que contribuye al posicionamiento en relación con otros elementos en la pantalla, pero no contribuye al "tamaño" de la ventana. El encabezado de la ventana se coloca absolutamente (lo que lo elimina del flujo de otros elementos, por lo que no hará que otros elementos como la lista desordenada se desplacen) y su parte superior se coloca -20px, lo que coloca el encabezado dentro del margen de la ventana. Finalmente, nuestro elemento ul se agrega a la ventana, y la altura se puede establecer al 100%, lo que hará que llene el cuerpo de la ventana (excluyendo el margen).

*,*:before,*:after
{
  box-sizing: border-box;
}

.window
{
  position: relative;
  top: 20px;
  left: 50px;
  margin-top: 20px;
  width: 150px;
  height: 150px;
}

.window-header
{
  position: absolute;
  top: -20px;
  height: 20px;
  border: 2px solid black;
  width: 100%;
}

ul
{
  border: 5px dashed gray;
  height: 100%;
}
<div class="window">
  <div class="window-header">Hey this is a header</div>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
  </ul>
</div>

TJ Rockefeller
fuente
1

Gracias, resolví la mía con tu ayuda, ajustándola un poco ya que quiero un div 100% de ancho 100% de altura (menos altura de una barra inferior) y sin desplazamiento en el cuerpo (sin piratear / ocultar barras de desplazamiento).

Para CSS:

 html{
  width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }
 body{
  position:relative;width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }
 div.adjusted{
  position:absolute;width:auto;height:auto;left:0px;right:0px;top:0px;bottom:36px;margin:0px;border:0px;padding:0px;
 }
 div.the_bottom_bar{
  width:100%;height:31px;margin:0px;border:0px;padding:0px;
}

Para HTML:

<body>
<div class="adjusted">
 // My elements that go on dynamic size area
 <div class="the_bottom_bar">
  // My elements that goes on bottom bar (fixed heigh of 31 pixels)
 </div>  
</div>  

Eso funcionó, oh sí, puse un valor un poco mejor en div.ajustada para la altura de la barra inferior que para la inferior, de lo contrario aparece la barra de desplazamiento vertical, ajusté el valor más cercano.

Esa diferencia se debe a que uno de los elementos en el área dinámica es agregar un agujero inferior adicional del que no sé cómo deshacerme ... es una etiqueta de video (HTML5), tenga en cuenta que puse esa etiqueta de video con este CSS ( así que no hay razón para que haga un agujero inferior, pero lo hace):

 video{
  width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }

El objetivo: tener un video que tome el 100% del navegador (y cambie de tamaño dinámicamente cuando se cambia el tamaño del navegador, pero sin alterar la relación de aspecto) menos un espacio inferior que utilizo para un div con algunos textos, botones, etc. (y validadores w3c y css, por supuesto).

EDITAR: Encontré la razón, la etiqueta de video es como texto, no un elemento de bloque, así que lo arreglé con este CSS:

 video{
  display:block;width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }

Tenga display:block;en cuenta la etiqueta de video.

Claudio
fuente
0

No estoy seguro de si esto funciona en su situación particular, pero he encontrado que el relleno en el div interno empujará el contenido dentro de un div si el div que lo contiene es de un tamaño fijo. Tendría que flotar o posicionar absolutamente su elemento de encabezado, pero de lo contrario, no he intentado esto para divs de tamaño variable.

dclowd9901
fuente