CSS: definindo largura / altura como porcentagem menos pixels

479

Estou tentando criar algumas classes CSS reutilizáveis ​​para obter mais consistência e menos confusão no meu site, e estou tentando padronizar uma coisa que uso com frequência.

Eu tenho um contêiner para o <div>qual não quero definir a altura (porque variará dependendo do local do site), e dentro dele há um cabeçalho <div>e, em seguida, uma lista não ordenada de itens, todos com CSS aplicado a eles.

Parece muito com isso:

Ferramenta

Quero que a lista não ordenada ocupe a sala restante no contêiner <div>, sabendo que o cabeçalho <div>é 18pxalto. Só não sei como especificar a altura da lista como "o resultado de 100%menos 18px".

Eu já vi essa pergunta em alguns outros contextos no SO, mas achei que valeria a pena perguntar novamente pelo meu caso particular. Alguém tem algum conselho nessa situação?

MegaMatt
fonte
6
Definir uma margem? __
kennytm
@KennyTM, presumo que você esteja sugerindo que eu coloque uma margem superior na minha lista não ordenada de, digamos, 17px. Mas isso apenas empurra a lista inteira para baixo; não faz com que ele encolha para permanecer no recipiente. Essencialmente, sua altura atual é mantida, mas é reduzida apenas em 17px. Isso não resolve o meu problema, mas acho que é um passo na direção certa, porque vi outras abordagens on-line que usavam essa técnica.
MegaMatt
Acabei de resolver esse problema na minha pergunta que publiquei aqui. stackoverflow.com/a/10420387/340947
Steven Lu
possível duplicata de CSS Como definir div altura 100% menos NPX
200_success

Respostas:

895

Sei que este é um post antigo, mas, como não foi sugerido, vale a pena mencionar que se você estiver escrevendo para navegadores compatíveis com CSS3, poderá usar calc:

height: calc(100% - 18px);

Vale a pena notar que nem todos os navegadores atualmente suportam a função CSS3 calc () padrão; portanto, a implementação de versões específicas da função pode ser necessária da seguinte maneira:

/* 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
fonte
5
Sim, isso é normal por enquanto.
Levi Botelho
1
Funciona para mim no IE10 e no Chrome 27. Senhor, você é o meu herói!
BrainSlugs83
3
@LeviBotelho My bad. Não havia espaço ao redor do operador.
usuário
20
Além disso, se você usar Less, é melhor compilar um arquivo lessc --strict-math=onpara não avaliar a expressão dentro do calc- apenas um problema que enfrentei e gastei muito tempo (a partir do Less 2.0.0)
Dmitry Wojciechowski
34
Note-se que os espaços em torno do sinal de menos não são opcionais: 100%-18px, 100%- 18pxe 100% -18pxnão funcionou em todos os navegadores que eu testei com.
Nisetama
71

Para uma abordagem um pouco diferente, você pode usar algo como isto na lista:

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

Isso funciona desde que o contêiner pai tenha position: relative;

Nicolas Galler
fonte
1
obrigado cara. o recipiente pai position: relative;estava me tropeçando.
Gthmb # 10/12
Para aqueles que se perguntam como eu: a posição precisa estar absoluteno contêiner infantil, não pode estar relative.
Greg
Continua (desculpe): No entanto, o pai precisa apenas de posicionamento (também pode ser absolute) e também precisa ser definido como 100% de altura.
Greg
1
Infelizmente, ele não funciona no Safari iOS 8. (Testado OK no navegador de ações do Android 4.1 e no padrão FF 34) :-(
Greg
Isso pode funcionar em alguns casos, mas também pode causar alguns problemas, pois elementos absolutamente posicionados são removidos do fluxo normal, consulte stackoverflow.com/a/12821537/4173303 .
Christophe Weis
26

Eu uso o Jquery para isso

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

vinculo o redimensionamento da janela para recalcá-lo sempre que a janela do navegador é redimensionada (se o tamanho do contêiner for alterado com o redimensionamento da janela)

$(window).resize(function() { setSizes(); });
puffpio
fonte
12
@puffpio, certamente o JS é uma maneira de fazer isso caso a caso. Mas como eu disse, estou tentando tornar o CSS genérico, para ser aplicado em várias páginas (que herdam de uma página mestra, aliás). Então, eu prefiro não usar JS. Mas obrigado pela resposta.
MegaMatt
7
Não coloque seu estilo dentro do JavaScript.
Greg
8
@greg, bem, ajudaria se o CSS tivesse alguns recursos mais úteis. Algumas das coisas pelas quais você precisa fazer hacks são ridículas.
Jonathan.
1
@puffpio Solução agradável mesmo. Mas eventos vinculativos na janela não são uma boa solução para mim. Se minha página tiver 3-4 ou mais desses elementos, tenho que atualizar a altura e a largura de cada elemento no manipulador de redimensionamento de janelas. Isso seria um pouco mais lento que a solução CSS.
precisa saber é o seguinte
1
"Não coloque seu xyz dentro do JavaScript" é como dizer "não suporta 80% do mercado de navegadores".
Beejor
16

Não defina a altura como porcentagem, apenas defina top=0e bottom=0, assim:

#div {
   top: 0; bottom: 0;
   position: absolute;
   width: 100%;
}
kaleazy
fonte
Você poderia explicar por que não definir a altura em um percentual?
Shrikant Sharat
@ShrikantSharat Isso faz parte da especificação de formatação visual para elementos absolutamente posicionados. Esta solução aproveita a possibilidade 5., "'height' é 'auto', 'top' e 'bottom' não são 'auto', então os valores 'auto' para 'margin-top' e 'margin-bottom' são definidos para 0 e resolva para 'altura' ". O navegador pode calcular a altura do elemento porque sabe a que distância deve estar a parte superior e inferior do pai.
Ross Allen
Isso não parece funcionar, pelo menos no Firefox. Mesmo com todos os elementos pai definidos como height:100%e display:block. Caso contrário, seria uma solução muito elegante. Curiosamente, margin:autotambém falha ao centralizar verticalmente um objeto de largura fixa, mesmo que ele funcione bem horizontalmente.
Beejor
8

Presumindo 17px de altura do cabeçalho

Listar css:

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

CSS do cabeçalho:

height: 17px;
float: left;
width: 100%;
ghoppe
fonte
Sim, era nisso que eu estava falando.
dclowd9901
1
Isso não funcionou tão bem quanto eu esperava. Minha lista não ordenada está sendo iniciada abaixo dessa div de cabeçalho. A div do cabeçalho está ocupando espaço no contêiner, portanto, colocar 17px na parte superior da lista apenas empurrará 17px para baixo de onde já está na foto acima, e não 17px da parte superior da div do contêiner. Aqui está uma foto do que recebo com o CSS fornecido na resposta acima: i41.tinypic.com/mcuk1x.jpg . No entanto, agradeço o esforço e, se achar que estou perdendo algo, entre em contato. Btw, eu tenho overflow: auto na lista não ordenada também.
MegaMatt
2
Seria muito mais fácil solucionar esse problema se você publicasse CSS real para que possamos ver exatamente quais interações estão acontecendo. Outra coisa que você pode tentar é a margem superior negativa no seu ul. ul { margin-top: -17px; }
ghoppe
7
  1. Use margens negativas no elemento que você gostaria de menos pixels. (elemento desejado)
  2. Faça overflow:hidden;no elemento que contém
  3. Mude para overflow:auto;o elemento desejado.

Funcionou para mim!

desanimar
fonte
3
Isso funcionou como um encanto para mim (eu nem precisei mudar o estouro). Ótima solução, obrigado!
Aaj
1
Totalmente incrível! Apreciamos o aprendizado da popular solução "CSS calc", mas isso é muito mais simples e tem um suporte mais amplo ao navegador. Margens negativas perfeitas!
Simon Steinberger
Esta é uma solução muito elegante e compatível. A principal desvantagem é que o elemento deve ter uma altura fixa. Caso contrário, você precisará usar o JS ou calc()centralizá-lo no tempo de execução. Mas, em muitos casos, isso não deve ser um problema. Outra coisa a ter em mente é que o uso de muitos valores negativos para margens, preenchimento e compensações pode causar confusão ao depurar o código posteriormente, portanto, tente manter as coisas simples.
Beejor
5

Tente o tamanho da caixa. Para a 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 o cabeçalho:

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

Obviamente, o contêiner pai deve ter algo como:

position: relative;
Leve
fonte
3

Outra maneira de alcançar o mesmo objetivo: caixas flexíveis . Torne o contêiner uma caixa flexível da coluna e você terá toda a liberdade para permitir que alguns elementos tenham tamanho fixo (comportamento padrão) ou preencham / encolhem no espaço do contêiner (com flex-grow: 1 e flex- encolher: 1).

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

Consulte https://jsfiddle.net/2Lmodwxk/ (tente estender ou reduzir a janela para perceber o efeito)

Nota: você também pode usar a propriedade taquigrafia:

   flex:1 1 auto;
tarulen
fonte
Acredito que essa seja a melhor resposta hoje, porque significa não ter que especificar uma altura definida para o cabeçalho div (de 18px na minha pergunta original) e não ter que subtrair coisas como preenchimento e margem. Não há pixels definidos, e tudo se cuida.
MegaMatt
2

Eu tentei algumas das outras respostas, e nenhuma delas funcionou exatamente como eu queria. Nossa situação era muito semelhante quando tínhamos um cabeçalho de janela e a janela era redimensionável com imagens no corpo da janela. Queríamos bloquear a proporção do redimensionamento sem precisar adicionar cálculos para levar em conta o tamanho fixo do cabeçalho e ainda assim fazer com que a imagem preencha o corpo da janela.

Abaixo, criei um snippet muito simples que mostra o que acabamos fazendo, que parece funcionar bem para a nossa situação e deve ser compatível com a maioria dos navegadores.

No elemento window, adicionamos uma margem de 20 px que contribui para o posicionamento em relação a outros elementos na tela, mas não contribui para o "tamanho" da janela. O cabeçalho da janela é então posicionado absolutamente (o que o remove do fluxo de outros elementos, para que não faça com que outros elementos como a lista não ordenada sejam deslocados) e seu topo é posicionado -20px, que coloca o cabeçalho dentro da margem da janela. Finalmente, nosso elemento ul é adicionado à janela e a altura pode ser definida como 100%, o que fará com que preencha o corpo da janela (excluindo a margem).

*,*: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
fonte
1

Obrigado, resolvi o meu com a sua ajuda, aprimorando-o um pouco, pois quero uma div 100% largura 100% de altura (menos altura de uma barra inferior) e nenhuma rolagem no corpo (sem hack / ocultar barras de rolagem).

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>  

Isso fez o truque, oh sim, eu coloquei um valor um pouco melhor em div.ajustado para a parte inferior do que para a altura da barra inferior, caso contrário a barra de rolagem vertical aparece, eu ajustei para ser o valor mais próximo.

Essa diferença é porque um dos elementos na área dinâmica está adicionando um orifício extra extra do qual eu não sei como me livrar ... é uma tag de vídeo (HTML5), observe que coloquei essa tag de vídeo com este css ( então não há razão para fazer um furo no fundo, mas sim):

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

O objetivo: ter um vídeo que use 100% do navegador (e redimensione dinamicamente quando o navegador é redimensionado, mas sem alterar a proporção) menos um espaço inferior que eu uso para uma div com alguns textos, botões, etc. (e validadores w3c & css, é claro).

EDIT: eu encontrei o motivo, tag de vídeo é como texto, não um elemento de bloco, então eu corrigi-lo com este css:

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

Observe a display:block;tag no vídeo.

Claudio
fonte
0

Não tenho certeza se isso funciona em sua situação específica, mas descobri que o preenchimento na div interna empurrará o conteúdo para dentro de uma div se a div contida tiver um tamanho fixo. Você precisaria flutuar ou posicionar absolutamente seu elemento de cabeçalho, mas, caso contrário, não tentei isso para divs de tamanho variável.

dclowd9901
fonte