Pourquoi les pourcentages de marge / remplissage en CSS sont-ils toujours calculés par rapport à la largeur?

130

Si vous regardez la spécification du modèle de boîte CSS , vous observerez ce qui suit:

Le pourcentage de [marge] est calculé par rapport à la largeur du bloc contenant de la boîte générée. Notez que cela est également vrai pour «margin-top» et «margin-bottom». Si la largeur du bloc conteneur dépend de cet élément, la mise en page résultante n'est pas définie dans CSS 2.1. (c'est moi qui souligne)

C'est en effet vrai. Mais pourquoi ? Qu'est-ce qui obligerait quelqu'un à le concevoir de cette façon? Il est facile de penser à des scénarios où vous voulez, par exemple, une certaine chose doit toujours être 25% en dessous du haut de la page, mais il est difficile de trouver une raison pour laquelle vous voudriez que le remplissage vertical soit relatif à la taille horizontale de le parent.

Voici un exemple du phénomène auquel je fais référence:

<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
la source
3
Ma première pensée est que cela provoquerait une ambiguïté quant à ce que margin: 25%signifie réellement. Ce ne serait pas une marge égale, même si le code le suggère. Je n'ai aucune preuve à l'appui, mais cela semble raisonnable.
Ryan Kinal
4
jsFiddle de mes pensées . La hauteur est beaucoup plus variable que la largeur, de sorte que les marges se déplaceraient de manière difficile à prévoir chaque fois que le contenu changeait.
RichardTowers
1
@Ryan: C'est vrai, ce ne serait pas une marge égale, mais encore une fois, si vous dites que height: 10%; width: 10%vous n'obtiendrez pas d'élément carré non plus.
mqp
4
Selon dev.w3.org/csswg/css3-box/#the-margin-properties, il déclare 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).donc ça va dans les deux sens
Adam Sweeney
1
@Ryan Je pense que nous avons un gagnant. Regardez ici pour une démo - jsFiddle
RichardTowers

Réponses:

60

Transférer mon commentaire à une réponse, car cela a un sens logique. Cependant, veuillez noter qu'il s'agit d'une conjecture infondée. Le raisonnement réel pour lequel la spécification est écrite de cette manière est encore, techniquement, inconnu.

La hauteur de l'élément est définie par la hauteur des enfants. Si un élément a padding-top: 10% (par rapport à la hauteur du parent), cela affectera la hauteur du parent. Comme la hauteur de l'enfant dépend de la hauteur du parent et que la hauteur du parent dépend de la hauteur de l'enfant, nous aurons soit une hauteur inexacte, soit une boucle infinie. Bien sûr, cela n'affecte que le cas où offset parent === parent, mais quand même. C'est un cas étrange difficile à résoudre.

Mise à jour: les deux dernières phrases peuvent ne pas être tout à fait exactes. La hauteur de l'élément feuille (enfant sans enfant) a un effet sur la hauteur de tous les éléments au-dessus, donc cela affecte de nombreuses situations différentes.

Ryan Kinal
la source
1
Notez qu'il existe des exceptions à cette règle - la section 10.6 de CSS2.1 couvre presque toutes les instances de calcul des hauteurs pour différents types de boîtes. Et en effet, les cas qui impliquent une codépendance entre le parent et l'enfant en hauteur ont tendance à conduire à un comportement indéfini.
BoltClock
1
@sanjaypoyzer Je suppose que dans le cas le plus simple, les navigateurs calculent les largeurs de bloc de l'extérieur vers l'intérieur (de la racine aux pointes), puis insèrent le contenu dans ces blocs pour déterminer leurs hauteurs (conseils à la racine).
sam
9
Pourquoi le W3C fait-il constamment cela? Apparemment, «sémantique» pour le W3C équivaut à devoir s'adresser à des personnes qui ne peuvent pas penser logiquement, ce qui n'a aucun sens. C'est comme se plaindre de la confusion et de la stupidité des gens dans le monde, puis répandre plus de confusion et de stupidité. Le raisonnement que vous avez fourni n'a pas de sens: le même argument s'applique aux largeurs, mais cela fonctionne très bien. Tout ce qui serait nécessaire est de définir un contexte et une direction dans lesquels les hauteurs sont déterminées, à moins que la hauteur du parent ne soit définie en pixels ou en quelque chose d'immuable, alors il n'y a pas beaucoup de problème.
u353
3
Cette dépendance est une formule algébrique qui a une solution et ne résultera pas en une boucle infinie à moins que vous ne choisissiez de la résoudre d'une manière qui ne respecte pas cela. Disons que la taille des parents est h_p. Un haut de remplissage de 10% fera la hauteur de l'enfant h_c = h_ci + 0.1h_p, où h_ciest la hauteur intérieure de l'enfant et ne dépend pas de la hauteur du parent. Pour un enfant, vous trouvez simplement la hauteur du parent à partir de l'équation h_p = h_ci + 0.1h_p=> h_p = h_ci / 0.9. Vous pouvez toujours le faire, quel que soit le nombre d'enfants que vous avez, même pour un rembourrage différent. C'est juste une inconnue ( h_p) et une équation.
jeteon
2
Je ne pense pas que le calcul infini en soit la raison. Une raison simple est que: l'utilisation des widthrésultats dans le même problème horizontalement.
Joy du
30

Pour que la marge "n%" (et le remplissage) soient les mêmes pour margin-top / margin-right / margin-bottom / margin-left, les quatre doivent être relatifs à la même base. Si haut / bas utilisait une base différente de gauche / droite ', alors la marge "n%" (et le remplissage) ne signifierait pas la même chose sur les quatre côtés.

(Notez également que le fait d'avoir la marge supérieure / inférieure par rapport à la largeur permet un étrange hack CSS qui vous permet de spécifier une boîte avec un rapport hauteur / largeur inchangé ... même si la boîte est redimensionnée.)

Chuck Kollars
la source
... une réponse étonnamment simple. Bien que toutes les conjectures ci-dessus aient du mérite, c'est un très bon point. Il semble peut-être un cas où plusieurs solutions seraient correctes, alors ils ont juste choisi la plus susceptible d'être utilisée ... peut-être?
dudewad
1
Je suppose que ce serait bien d'avoir un élément supplémentaire dans la syntaxe pour que vous puissiez écrire say padding: n [type]. Donc, vous auriez padding: 5% logical(ce que l'OP suggère) par opposition au padding: 5% widthdeuxième paramètre par défaut à "width" pour une compatibilité ascendante.
jeteon
5
"n% de marge (et de remplissage) ne signifierait pas la même chose sur les quatre côtés" - Mais pourquoi ce serait un problème?
Herbertusz
4

Je vote pour la réponse de @ChuckKollars après avoir joué avec ce JSFiddle (sur Chrome 46.0.2490.86) et me référer à ce post (écrit en chinois).


Une raison majeure contre la conjecture de calcul infini est que: utiliser des widthfaces le même problème de calcul infini .

Jetez un œil à ce JSFiddle , l' parentaffichage est inline-block, qui est éligible pour définir une marge / un remplissage sur celui-ci. Le childa une valeur de marge 20%. Si nous suivons la conjecture de calcul infinie :

  1. La largeur du childdépend de laparent
  2. La largeur du parentdépend de lachild

Mais en conséquence, Chrome arrête le calcul quelque part, ce qui entraîne:

entrez la description de l'image ici

Si vous essayez d'agrandir le panneau "résultat" horizontalement sur le JSFiddle, vous constaterez que leur largeur ne changera pas. Veuillez noter que le contenu de childest enveloppé dans deux lignes (pas, disons, une ligne), pourquoi? Je suppose que Chrome le code en dur quelque part. Si vous modifiez le childcontenu pour le rendre plus ( JSFiddle ), vous constaterez que tant qu'il y a de l'espace supplémentaire horizontalement, Chrome conserve le contenu sur deux lignes.

Nous pouvons donc voir: il existe un moyen d'empêcher le calcul infini .


Je suis d'accord avec la conjecture que: cette conception consiste simplement à conserver les quatre valeurs de marge / remplissage basées sur la même mesure.

cet article (écrit en chinois) propose également une autre raison: c'est à cause de l'orientation lecture / composition. Nous lisons de haut en bas, avec la largeur fixe et la hauteur infinie (virtuellement).

Joie
la source
C'est parce que les pages Web défilent verticalement dans une direction généralement infinie; donc la largeur peut être calculée à partir de la largeur de la fenêtre du navigateur. La seule façon dont les éléments sont plus larges que l'écran est de spécifier que leur largeur est plus grande. Les éléments de bloc typiques ont une largeur de 100% et se développent automatiquement dans le sens vertical (inconnu). C'est pourquoi la largeur n'a pas le même problème.
Lucent Fox
3

Je me rends compte que l'OP demande pourquoi la spécification CSS définit les pourcentages de marge haut / bas comme un% de la largeur (et non, comme on pourrait le supposer, la hauteur), mais j'ai pensé qu'il pourrait également être utile de publier une solution potentielle.

La plupart des navigateurs modernes prennent désormais en charge vw et vh, ce qui vous permet de spécifier des numéros de marge par rapport à la largeur et à la hauteur de la fenêtre.

100vw / 100vh équivaut à 100% de largeur / 100% de hauteur (respectivement) s'il n'y a pas de barre de défilement; s'il y a une barre de défilement, les numéros de la fenêtre n'en tiennent pas compte (contrairement aux nombres%). Heureusement, presque tous les navigateurs utilisent des barres de défilement de 17 pixels ( voir ici ), vous pouvez donc utiliser la fonction css calc pour en tenir compte. Si vous ne savez pas si une barre de défilement apparaîtra ou non, cette solution ne fonctionnera pas.

Par exemple: en supposant qu'il n'y a pas de barre de défilement horizontale, une marge supérieure de 50% de la hauteur pourrait être définie comme "margin-top: 50vh;". Avec une barre de défilement horizontale, cela pourrait être défini comme "margin-top: calc (0.5 * (100vh - 17px));" (rappelez-vous que les opérateurs moins et plus dans calc nécessitent des espaces des deux côtés!).

VKK
la source
1
C'est une solution de contournement utile dans de nombreux cas, mais il existe de nombreux exemples où cela ne fonctionne pas (par exemple, si vous essayez d'adapter le contenu proportionnellement à la taille d'un conteneur flexbox qui grandit pour remplir l'espace disponible alors qu'il y a une autre boîte avec contenu variable).
Jules