Por que as porcentagens de margem / preenchimento no CSS são sempre calculadas em relação à largura?

130

Se você observar as especificações do modelo da caixa CSS , observará o seguinte:

A porcentagem [margem] é calculada com relação à largura do bloco contendo a caixa gerada. Observe que isso também é válido para 'margin-top' e 'margin-bottom'. Se a largura do bloco contido depender desse elemento, o layout resultante será indefinido no CSS 2.1. (ênfase minha)

Isto é realmente verdade. Mas porque ? O que diabos obrigaria alguém a projetá-lo dessa maneira? É fácil pensar em cenários em que você deseja, por exemplo, uma certa coisa sempre a 25% abaixo da parte superior da página, mas é difícil encontrar qualquer motivo para desejar que o preenchimento vertical seja relativo ao tamanho horizontal de o pai.

Aqui está um exemplo do fenômeno ao qual estou me referindo:

<div style="border: 1px solid red; margin: 0; padding: 0; width: 200px; height: 800px;">
  This div is 200x800.
  <div style="border: 1px solid blue; margin: 10% 0 0 10%;">
    This div has top-margin of 10% and left-margin of 10% with respect to its parent.
  </div>
</div>

http://jsfiddle.net/8JDYD/

mqp
fonte
3
Meu pensamento inicial é que isso causaria ambiguidade quanto ao que margin: 25%realmente significa. Não seria uma margem uniforme, mesmo que o código sugira que seja. Não tenho evidências para apoiar isso, mas parece razoável.
Ryan Kinal
4
jsFiddle dos meus pensamentos . A altura é muito mais variável do que a largura, portanto, as margens mudam de maneiras difíceis de prever sempre que o conteúdo é alterado.
RichardTowers
1
@ Ryan: Isso é verdade, não seria uma margem uniforme, mas, novamente, se você disser height: 10%; width: 10%que não receberá um elemento quadrado, também.
Mqp 12/06/12
4
De acordo com dev.w3.org/csswg/css3-box/#the-margin-properties afirma Note that in a horizontal flow, percentages on ‘margin-top’ and ‘margin-bottom’ are relative to the width of the containing block, not the height (and in vertical flow, ‘margin-left’ and ‘margin-right’ are relative to the height, not the width).Assim vai ambas as maneiras
Adam Sweeney
1
@ Ryan Acho que temos um vencedor. Veja aqui uma demonstração - jsFiddle
RichardTowers 12/12

Respostas:

60

Transferindo meu comentário para uma resposta, porque faz sentido lógico. No entanto, observe que essa é uma conjectura infundada. O raciocínio real do motivo pelo qual a especificação é escrita dessa maneira ainda é tecnicamente desconhecido.

A altura do elemento é definida pela altura dos filhos. Se um elemento tiver o padding-top: 10% (em relação à altura do pai), isso afetará a altura do pai. Como a altura do filho depende da altura do pai, e a altura do pai depende da altura do filho, teremos uma altura imprecisa ou um loop infinito. Claro, isso afeta apenas o caso em que pai deslocamento === pai, mas ainda assim. É um caso estranho e difícil de resolver.

Atualização: as últimas frases podem não ser totalmente precisas. A altura do elemento folha (filho sem filhos) afeta a altura de todos os elementos acima dele, portanto, isso afeta muitas situações diferentes.

Ryan Kinal
fonte
1
Observe que há exceções a esta regra - a seção 10.6 do CSS2.1 abrange quase todas as instâncias de cálculo de alturas para vários tipos de caixas. E, de fato, casos que envolvem uma co-dependência entre pai e filho em altura tendem a levar a um comportamento indefinido.
BoltClock
1
@sanjaypoyzer Eu acho que, no caso mais simples, os navegadores calculam as larguras dos blocos de fora para dentro (raiz às dicas) e depois fluem o conteúdo para esses blocos para determinar suas alturas (dicas para a raiz).
sam
9
Por que o W3C constantemente faz isso? Aparentemente, 'semântica' para o W3C é o equivalente a ter que atender pessoas que não conseguem pensar logicamente, o que não faz sentido. É como reclamar sobre como as pessoas são confusas e burras no mundo e espalhar mais confusão e estupidez. O raciocínio que você forneceu não faz sentido: o mesmo argumento se aplica às larguras, mas isso funciona muito bem. Tudo o que seria necessário é definir um contexto e uma direção que as alturas sejam determinadas, a menos que a altura do pai seja definida em pixels ou algo imutável, então não há muito problema.
u353
3
Essa dependência é uma fórmula algébrica que tem uma solução e não resultará em um loop infinito, a menos que você decida resolvê-la de uma maneira que não respeite isso. Digamos que a altura dos pais seja h_p. Um preenchimento de 10% aumentará a altura da criança h_c = h_ci + 0.1h_p, onde h_ciestá a altura interna da criança e não depende da altura dos pais. Para um filho, basta encontrar a altura dos pais na equação h_p = h_ci + 0.1h_p=> h_p = h_ci / 0.9. Você sempre pode fazer isso, não importa quantos filhos você tenha, mesmo para diferentes estofamentos. É apenas uma incógnita ( h_p) e uma equação.
jeteon
2
Não acho que o cálculo infinito seja o motivo. Uma razão simples é a seguinte: usar widthresultados no mesmo problema horizontalmente.
Joy
30

Para que a margem "n%" (e preenchimento) seja a mesma para margem superior / margem direita / margem inferior / margem esquerda, todos os quatro devem ser relativos à mesma base. Se a parte superior / inferior usasse uma base diferente da esquerda / direita ', a margem "n%" (e o preenchimento) não significariam a mesma coisa nos quatro lados.

(Observe também que a margem superior / inferior em relação à largura permite um hack CSS estranho que permite especificar uma caixa com uma proporção imutável ... mesmo que a caixa seja redimensionada.)

Chuck Kollars
fonte
... uma resposta surpreendentemente simples. Embora toda a conjectura acima tenha mérito, esse é um ponto muito bom. Parece possivelmente um caso em que várias soluções estariam corretas, de modo que apenas escolheram a mais provável a ser usada ... talvez?
Dudewad
1
Suponho que seria bom ter um elemento extra na sintaxe para que você possa escrever digamos padding: n [type]. Então, você teria padding: 5% logical(o que o OP sugere) em oposição ao padding: 5% widthsegundo parâmetro, que é padronizado como "width" para compatibilidade com versões anteriores.
jeteon
5
"margem de n% (e preenchimento) não significaria a mesma coisa nos quatro lados" - Mas por que isso seria um problema?
Herbertusz 28/03
4

Eu voto na resposta do @ChuckKollars depois de jogar com este JSFiddle (no Chrome 46.0.2490.86) e me referir a esta postagem (escrita em chinês).


Um dos principais motivos contra a conjectura de cálculo infinito é que: o uso widthenfrenta o mesmo problema de cálculo infinito .

Dê uma olhada neste JSFiddle , a parenttela inline-blocké qualificada para definir margem / preenchimento nela. O childvalor da margem 20%. Se seguirmos a conjectura de cálculo infinito :

  1. A largura do childdepende daparent
  2. A largura do parentdepende dachild

Mas, como resultado, o Chrome interrompe o cálculo em algum lugar, resultando:

insira a descrição da imagem aqui

Se você tentar expandir o painel "resultado" horizontalmente no JSFiddle, verá que a largura deles não será alterada. Observe que o conteúdo no childé dividido em duas linhas (não, digamos, uma linha), por quê? Eu acho que o Chrome apenas codifica em algum lugar. Se você editar o childconteúdo para torná-lo mais ( JSFiddle ), descobrirá que, enquanto houver espaço extra horizontalmente, o Chrome manterá o conteúdo em duas linhas.

Então, podemos ver: existe uma maneira de impedir o cálculo infinito .


Concordo com a conjectura de que: esse projeto serve apenas para manter os quatro valores de margem / preenchimento com base na mesma medida.

este post (escrito em chinês) também propõe outro motivo: é por causa da orientação da leitura / composição. Lemos de cima para baixo, com a largura fixa e a altura infinita (virtualmente).

Alegria
fonte
Isso ocorre porque as páginas da web rolam verticalmente em uma direção geralmente infinita; para que a largura possa ser calculada a partir da largura da janela do navegador. A única maneira de os elementos serem mais largos que a tela é se você especificar sua largura para ser maior. Os elementos de bloco típicos têm largura de 100% e se expandem automaticamente na direção vertical (desconhecida). É por isso que a largura não tem o mesmo problema.
Lucent Fox
3

Percebo que o OP está perguntando por que a especificação CSS define as porcentagens da margem superior / inferior como um% da largura (e não, como seria de se supor, altura), mas achei que também seria útil publicar uma solução em potencial.

A maioria dos navegadores modernos oferece suporte a vw e vh agora, o que permite especificar números de margem em relação à largura e altura da janela de exibição.

100vw / 100vh é igual a 100% de largura / 100% de altura (respectivamente) se não houver barra de rolagem; se houver uma barra de rolagem, os números da janela de visualização não são responsáveis ​​por isso (enquanto os% de números fazem). Felizmente, quase todos os navegadores usam tamanhos de barra de rolagem de 17px ( veja aqui ), para que você possa usar a função css calc para explicar isso. Se você não souber se uma barra de rolagem será exibida ou não, esta solução não funcionará.

Por exemplo: Supondo que nenhuma barra de rolagem horizontal, uma margem superior de 50% da altura, poderia ser definida como "margem superior: 50vh;". Com uma barra de rolagem horizontal, isso pode ser definido como "margem superior: calc (0,5 * (100vh - 17px))"; (lembre-se de que os operadores menos e mais no calc exigem espaços de ambos os lados!).

VKK
fonte
1
É uma solução alternativa útil em muitos casos, mas há muitos exemplos em que não funciona (por exemplo, se você está tentando ajustar o conteúdo proporcionalmente ao tamanho de um contêiner flexbox que cresce para preencher o espaço disponível enquanto há outra caixa com conteúdo variável).
Jules