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?


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

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

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;


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

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

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

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!


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;

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;

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>


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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.