为什么CSS中的边距/填充百分比总是按宽度计算?


130

如果您查看CSS盒模型规范,将会观察到以下内容:

[margin]百分比是相对于生成的框的包含块的宽度计算的。请注意,“ margin-top”和“ margin-bottom”也是如此。如果包含块的宽度取决于此元素,则CSS 2.1中未定义结果布局。 (强调我的)

确实是这样。但是为什么呢?到底什么会迫使任何人以这种方式设计它?很容易想到您想要的方案,例如,某件事总是比页面顶部低25%,但是很难提出您希望垂直填充相对于水平尺寸相对的任何原因。父母。

这是我所指现象的一个示例:

<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/


3
我最初的想法是,这将对margin: 25%实际含义造成歧义。即使代码表明确实如此,这也不是一个均匀的利润。我没有证据支持这一点,但这似乎是合理的。
Ryan Kinal 2012年

4
jsFiddle我的想法。高度比宽度可变得多,因此每当内容更改时,边缘都会以难以预测的方式变化。
RichardTowers 2012年

1
@Ryan:是的,这不是一个相等的余量,但是如果您说height: 10%; width: 10%您也不会得到一个正方形元素,那还是一样。
mqp 2012年

4
根据dev.w3.org/csswg/css3-box/#the-margin-properties的 陈述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).,它是双向的-Adam
Sweeney

1
@Ryan我想我们有赢家。Looky在这里观看演示-jsFiddle
RichardTowers 2012年

Answers:


60

将我的评论转为答案,因为这是合乎逻辑的。但是,请注意,这是没有根据的推测。为何以这种方式编写规范的实际原因在技术上仍然未知。

元素高度由子代的高度定义。如果一个元素的padding-top:10%(相对于父级高度),那将影响父级的高度。由于孩子的身高取决于父母的身高,而父母的身高则取决于孩子的身高,因此我们的身高可能会不正确,或者会无限循环。当然,这仅会影响offset parent === parent,但仍然如此。这是一个很难解决的奇怪情况。

更新:最后两句话可能并不完全准确。叶元素(没有子元素的子元素)的高度会影响其上方所有元素的高度,因此会影响许多不同的情况。


1
请注意,此规则也有例外-CSS2.1的10.6节几乎涵盖了各种类型的盒子的计算高度的所有实例。实际上,涉及父母和孩子身高之间相互依存的情况往往会导致不确定的行为。
BoltClock

1
@sanjaypoyzer我想在最简单的情况下,浏览器从外部计算块的宽度(从根到尖端),然后将内容流到这些块中以确定它们的高度(从根到尖端)。
2013年

9
为什么W3C会不断这样做?显然,对W3C的“语义化”等同于必须迎合无法逻辑思考的人们,这是没有道理的。这就像在抱怨世界上的人们多么困惑和愚蠢,然后散布更多的困惑和愚蠢。您提供的推理没有意义:相同的参数适用于宽度,但效果很好。所需要做的就是设置确定高度的上下文和方向,除非以像素或不可变的形式设置父对象的高度,那么问题就不多了。
u353 2014年

3
这种依赖关系是一个代数公式,它具有解决方案,并且不会导致无限循环,除非您选择以不遵守此方式的方式来解决它。假设父母的身高是h_p。填充顶部的10%将使孩子的高度成为h_c = h_ci + 0.1h_p,这h_ci是孩子的内部高度,而不取决于父级的高度。对于一个孩子,您只需从等式h_p = h_ci + 0.1h_p=>中找到父母的身高h_p = h_ci / 0.9。无论您有多少个孩子,即使您使用了不同的填充,也可以始终执行此操作。它只是一个未知数(h_p)和一个方程式。
jeteon 2015年

2
我不认为无限计算是原因。一个简单的原因是:使用width水平会导致相同的问题。
2015年

30

为了使“ n%”边距(和边距) margin-top / margin-right / margin-bottom / margin-left相同,所有这四个必须相对于同一基准。如果“顶部/底部”使用的基数不同于“左/右”,则“ n%”的边距(和填充)在所有四个方面都不会是同一件事。

(还请注意,具有相对于宽度的顶部/底部边距会启用怪异的CSS技巧,使您可以指定长宽比不变的盒子,即使重新缩放了盒子。)


令人惊讶的简单答案 尽管以上所有猜想都有其优点,但这是一个很好的观点。似乎有可能多个解决方案都是正确的,因此他们只是选择了最可能使用的一种解决方案。
dudewad 2015年

1
我想在语法中增加一个元素会很不错,这样您就可以写say padding: n [type]。因此,您将拥有padding: 5% logical(OP所建议的),而padding: 5% width第二个参数为了向后兼容而默认设置为“ width”。
jeteon 2015年

5
“ n%的利润率(和填充)在所有四个方面都不会意味着同一件事”-但是为什么这会成为问题?
Herbertusz

4

在玩完此JSFiddle(在Chrome 46.0.2490.86上)并提及此帖子(用中文编写)后,我投票给@ChuckKollars答案。


反对无限计算猜想的主要原因是:使用width面临着相同的无限计算问题。

看看此JSFiddleparent显示为inline-block,可以在其上定义边距/填充。在child有边距值20%。如果我们遵循无限计算猜想:

  1. 的宽度child取决于parent
  2. 的宽度parent取决于child

但是结果是,Chrome在某处停止了计算,结果是:

在此处输入图片说明

如果您尝试在JSFiddle上水平扩展“结果”面板,则会发现它们的宽度不会改变。请注意,中的内容child分为两行(不是一行),为什么?我认为Chrome只是在某个地方对其进行硬编码。如果您编辑child内容以使其内容更多(JSFiddle),您会发现,只要在水平方向上有多余的空间,Chrome就会将内容保留两行。

因此我们可以看到:有某种方法可以防止无限计算


我同意这样的推测:这种设计只是为了基于相同的度量来保持四个边距/填充值。

这篇文章(中文)也提出了另一个原因是:这是由于阅读/排版的方向。我们从上到下阅读,宽度固定,高度无限(实际上)。


这是因为网页通常沿无限的方向垂直滚动;因此可以根据浏览器窗口的宽度来计算宽度。如果将元素的宽度指定为更大,则唯一的方式是使其比屏幕更宽。典型的块元素的宽度为100%,并且会在垂直方向上自动扩展(未知)。这就是为什么宽度没有相同的问题。
Lucent Fox

3

我意识到OP正在问为什么 CSS规范将上下边距百分比定义为宽度的百分比(而不是假定的高度百分比),但是我认为发布潜在的解决方案也可能有用。

现在,大多数现代浏览器都支持vw和vh,这使您可以针对视口宽度和视口高度指定边距数字。

如果没有滚动条,则100vw / 100vh分别等于100%宽度/ 100%高度;如果有滚动条,则视口编号不占此比例(%占比例)。幸运的是,几乎所有浏览器都使用17px的滚动条大小(请参见此处),因此您可以使用css calc函数来解决此问题。如果您不知道是否会出现滚动条,则此解决方案将不起作用。

例如:假设没有水平滚动条,则可以将高度的50%的上​​边距定义为“ margin-top:50vh;”。使用水平滚动条,可以将其定义为“ margin-top:calc(0.5 *(100vh-17px));”。(请记住,calc中的减号和加号运算符在两边都需要空格!)。


1
在许多情况下,这是一个有用的解决方法,但是有许多示例无法解决问题(例如,如果您要使内容与Flexbox容器的大小成正比,那么该容器会逐渐增加以填充可用空间,而另一个盒子可变内容)。
朱尔斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.