Como definir a margem ou o preenchimento como porcentagem da altura do contêiner pai?

236

Eu estava estourando meu cérebro sobre a criação de um alinhamento vertical em css usando o seguinte

.base{
        background-color:green;
	    width:200px;
	    height:200px;
	    overflow:auto;
	    position:relative;
	}

    .vert-align{
		padding-top:50%;
		height:50%;
	}
<!-- and used the following div structure. -->

    <div class="base">
       <div class="vert-align">
    	   Content Here
       </div>
    </div>

Embora isso parecesse funcionar para este caso, fiquei surpreso que, quando aumentava ou diminuía a largura da minha base div, o alinhamento vertical se encaixava. Eu esperava que, ao definir a propriedade padding-top, o padding fosse considerado como uma porcentagem da altura do contêiner pai, que é a base do nosso caso, mas o valor acima de 50% é calculado como uma porcentagem da largura. :(

Existe uma maneira de definir o preenchimento e / ou margem como uma porcentagem da altura, sem recorrer ao JavaScript?

Ryan
fonte

Respostas:

223

A correção é que sim, preenchimento vertical e margem são em relação ao largura, mas tope bottom não são.

Então, basta colocar uma div dentro de outra e, na div interna, use algo como top:50%(lembre- positionse de importar se ainda não funcionar)

Camilo Martin
fonte
3
topfunciona muito bem para redimensionar com base na altura do navegador / janela. Que tal ter um div embaixo desse div? Como você pode clearo 2º div estar abaixo do div que é deslocado para baixo por um top:50%??
Federico
Aprenda sth novo. Não sabia que o preenchimento vertical e a margem são relativos à largura antes. Obrigado.
shaosh
5
Também é possível adicionar uma div 'espaçador' com uma altura percentual definida. Parece um pouco sujo, mas funciona. veja esta resposta .
WCByrne
4
O que o eterno @%! ^? Por que a margem vertical e o preenchimento são relativos à largura ? QUE? Por que diabos essa decisão foi tomada?
temporary_user_name
Sua resposta é extremamente desproporcionalmente votada em comparação com a resposta de alain abaixo, que fornece uma solução geral, e eu achei muito útil e quase não a vi. top: 50%não é muito útil se você precisar alinhar o conteúdo por seu próprio centro.
Okovko
35

Resposta a uma pergunta um pouco diferente: Você pode usar vhunidades para colocar elementos no centro da janela de exibição :

.centerme {
    margin-top: 50vh;
    background: red;
}

<div class="centerme">middle</div>
willoller
fonte
Por que essa resposta não é votada mais? Há algo de errado em usar vh?
AlaNH
3
É porque não é uma resposta adaptada à pergunta original, é um truque útil que funciona apenas em alguns casos relacionados.
willoller
usar vhé quase o mesmo que porcentagens ... ainda pior em alguns casos. Esta resposta é irrelevante para a pergunta
vsync
33

Aqui estão duas opções para emular o comportamento necessário. Não é uma solução geral, mas pode ajudar em alguns casos. O espaçamento vertical aqui é calculado com base no tamanho do elemento externo, não no pai, mas esse tamanho em si pode ser relativo ao pai e, dessa forma, o espaçamento também será relativo.

<div id="outer">
    <div id="inner">
        content
    </div>
</div>

Primeira opção: use pseudoelementos, aqui os espaçamentos vertical e horizontal são relativos ao externo. Demo

#outer::before, #outer::after {
    display: block;
    content: "";
    height: 10%;
}
#inner {
    height: 80%;
    margin-left: 10%;
    margin-right: 10%;
}

Mover o espaçamento horizontal para o elemento externo o torna relativo ao pai do externo. Demo

#outer {
    padding-left: 10%;
    padding-right: 10%;
}

Segunda opção: use posicionamento absoluto. Demo

#outer {
    position: relative;
}
#inner {
    position: absolute;
    left: 10%;
    right: 10%;
    top: 10%;
    bottom: 10%;
}
do utilizador
fonte
10

Para tornar o elemento filho posicionado absolutamente a partir de seu elemento pai, você precisa definir a posição relativa no elemento pai E a posição absoluta no elemento filho.

Em seguida, no elemento filho 'top' é relativo à altura do pai. Então você também precisa 'traduzir' para cima a criança 50% da sua própria altura.

.base{
    background-color: green;
    width: 200px;
    height: 200px;
    overflow: auto;
    position: relative;
}
    
.vert-align {
    position: absolute;
    top: 50%;
    transform: translate(0, -50%);
}
    <div class="base">
        <div class="vert-align">
            Content Here
        </div>
    </div>

Existe outra solução usando a caixa flexível.

.base{
    background-color:green;
    width: 200px;
    height: 200px;
    overflow: auto;
    display: flex;
    align-items: center;
}
<div class="base">
    <div class="vert-align">
        Content Here
    </div>
</div>

Você encontrará vantagens / desvantagens para ambos.

alain
fonte
1
O uso da conversão é a única maneira real de mover um elemento verticalmente em relação ao seu próprio tamanho.
Eran Goldin
Obrigado por isso. Essa deve ser a resposta escolhida.
Wessam El Mahdy
6

Isso pode ser alcançado com a writing-modepropriedade Se você definir um elemento writing-modepara um modo de gravação vertical, como, por exemplo vertical-lr, os valores percentuais de preenchimento e margem de seus descendentes em ambas as dimensões, tornar-se-á relativo à altura em vez da largura.

Das especificações :

. . . as porcentagens nas propriedades de margem e preenchimento, sempre calculadas com relação à largura do bloco contendo no CSS2.1, são calculadas com relação ao tamanho embutido do bloco contendo no CSS3.

A definição de tamanho embutido :

Uma medida na dimensão embutida: refere-se à largura física (dimensão horizontal) nos modos de escrita horizontal e à altura física (dimensão vertical) nos modos de escrita vertical.

Exemplo, com um elemento redimensionável, em que as margens horizontais são relativas à largura e as margens verticais são relativas à altura.

.resize {
  width: 400px;
  height: 200px;
  resize: both;
  overflow: hidden;
}

.outer {
  height: 100%;
  background-color: red;
}

.middle {
  writing-mode: vertical-lr;
  margin: 0 10%;
  width: 80%;
  height: 100%;
  background-color: yellow;
}

.inner {
  writing-mode: horizontal-tb;
  margin: 10% 0;
  width: 100%;
  height: 80%;
  background-color: blue;
}
<div class="resize">
  <div class="outer">
    <div class="middle">
      <div class="inner"></div>
    </div>
  </div>
</div>

O uso de um modo de escrita vertical pode ser particularmente útil nas circunstâncias em que você deseja que a proporção de um elemento permaneça constante, mas que seu tamanho seja dimensionado em correlação com a altura em vez da largura.

James T
fonte
Esta é a IMO, seria a solução mais elegante, mas (a partir de 05/05/2018), os modos de gravação vertical não são bem suportados . Espero que isso mude em breve.
Zanerock 5/05
1
@zanerock Estou bastante certo de que a tabela de compatibilidade MDN está desatualizada. Por exemplo, ele diz que o Safari precisa do -webkit-prefixo, mas, de acordo com a caniuse , não é necessário o prefixo desde o Safari 11. Você tentou com algum navegador no qual não funcionava?
James T
1
Não, tenho que admitir que assumi que era bom, automatizado mesmo.
Zanerock
4

Outra maneira de centralizar o texto de uma linha é:

.parent{
  position: relative;
}

.child{
   position: absolute;
   top: 50%;
   line-height: 0;
}

ou apenas

.parent{
  overflow: hidden; /* if this ins't here the parent will adopt the 50% margin of the child */
}

.child{
   margin-top: 50%;
   line-height: 0;
}
Rawiro
fonte
0

Um estofamento de 50% não irá centralizar seu filho, ele o colocará abaixo do centro. Eu acho que você realmente quer 25% de preenchimento. Talvez você esteja ficando sem espaço à medida que seu conteúdo fica mais alto? Você também tentou definir o margin-top em vez de padding-top?

EDIT: Nevermind, diz o site w3schools

% Especifica o preenchimento em porcentagem da largura do elemento que contém

Então, talvez ele sempre use largura? Eu nunca tinha notado.

O que você está fazendo pode ser obtido usando display: table (pelo menos para navegadores modernos). A técnica é explicada aqui .

SpliFF
fonte
1
Obrigado Spliff. Entendo que meus 50% não centralizarão o conteúdo da div. Na verdade, tenho apenas uma linha de texto que precisa ser centralizada verticalmente. Muito obrigado pelo link!
Ryan
3
Se é apenas uma linha de texto que você considerou usar um tamanho ridiculamente grande line-height, ou seja, crie 200px de altura de linha?
SpliFF 14/02
@Ryan para centralizar o texto verticalmente, consulte stackoverflow.com/questions/8865458/…
user
seu link está quebrado
quemeful
0

Este é um bug muito interessante. (Na minha opinião, é um bug de qualquer maneira) Bom achado!

Sobre como configurá-lo, eu recomendaria a resposta de Camilo Martin. Mas, por que , eu gostaria de explicar um pouco se vocês não se importam.


Nas especificações CSS encontrei:


Porcentagens 'padding' : referem-se à largura do bloco contendo

... o que é estranho, mas tudo bem.

Assim, com pai width: 210pxe filho padding-top: 50%, recebo um valor calculado / calculado de padding-top: 96.5px- que não é o esperado 105px.

Isso ocorre no Windows (não tenho certeza sobre outros sistemas operacionais), o tamanho das barras de rolagem comuns é por padrão 17px × 100%(ou 100% × 17pxpara barras horizontais). Aqueles 17pxsão subtraídos antes de calcular o 50%, portanto 50% of 193px = 96.5px.

WoodrowShigeru
fonte
Há uma razão para o spec, e isso explica-lo melhor: hongkiat.com/blog/calculate-css-percentage-margins
manikanta