为啥 CSS 中的边距/填充百分比总是根据宽度计算?
Posted
技术标签:
【中文标题】为啥 CSS 中的边距/填充百分比总是根据宽度计算?【英文标题】:Why are margin/padding percentages in CSS always calculated against width?为什么 CSS 中的边距/填充百分比总是根据宽度计算? 【发布时间】:2012-06-15 18:19:14 【问题描述】:如果您查看CSS box model spec,您会看到以下内容:
[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/
【问题讨论】:
我最初的想法是它会导致margin: 25%
的实际含义模棱两可。即使代码表明是这样,它也不会是一个均匀的边距。我没有证据支持这一点,但这似乎是合理的。
jsFiddle of my thoughts。高度比宽度更易变化,因此每当内容发生变化时,它会使边距以难以预测的方式移动。
@Ryan:是的,这不会是一个均匀的边距,但话又说回来,如果你说height: 10%; width: 10%
,你也不会得到一个正方形元素。
根据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).
所以它是双向的
@Ryan 我认为我们有赢家。在这里寻找演示 - jsFiddle
【参考方案1】:
将我的评论转移到答案,因为它合乎逻辑。但请注意,这是毫无根据的猜想。以这种方式编写规范的实际原因在技术上仍然未知。
元素高度由元素的高度定义 孩子们。如果一个元素有 padding-top: 10% (相对于父元素 高度),这将影响父母的高度。由于 孩子的身高取决于父母的身高,而 父母的身高取决于孩子的身高,我们将 要么高度不准确,要么无限循环。当然,这只是 影响偏移父===父的情况,但仍然。这是一个 难以解决的奇案。
更新:最后几句话可能并不完全准确。叶元素(没有子元素的子元素)的高度会影响其上方所有元素的高度,因此这会影响许多不同的情况。
【讨论】:
请注意,这条规则也有例外——CSS2.1 的第 10.6 节几乎涵盖了计算各种类型盒子高度的所有实例。事实上,涉及父母和孩子身高相互依赖的情况往往会导致未定义的行为。 @sanjaypoyzer 我猜在最简单的情况下,浏览器会从外部计算块宽度(根到提示),然后将内容流入这些块以确定它们的高度(提示到根)。 为什么 W3C 经常这样做?显然,对 W3C 来说,“语义”就相当于必须迎合那些无法进行逻辑思考的人,这是没有意义的。这就像抱怨世界上的人是多么的混乱和愚蠢,然后传播更多的混乱和愚蠢。您提供的推理没有意义:同样的论点适用于宽度,但效果很好。所需要的只是设置确定高度的上下文和方向,除非父对象的高度以像素为单位设置或不可变,否则问题不大。 这种依赖是一个代数公式,它有一个解决方案,并且不会导致无限循环,除非您选择以不尊重这一点的方式解决它。假设父母身高是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
) 和一个方程式。
我不认为无限计算是原因。一个简单的原因是:使用width
会导致水平相同的问题。【参考方案2】:
要使“n%”边距(和填充)与 margin-top/margin-right/margin-bottom/margin-left 的 相同,所有四个必须相对于相同根据。如果 top/bottom 使用的 base 与 left/right' 不同,那么“n%”边距(和填充)在所有四个边上的含义都不相同。
(另请注意,相对于 width 的顶部/底部边距会启用一个奇怪的 CSS hack,它允许您指定一个具有不变纵横比的框……即使该框被重新缩放。 )
【讨论】:
...一个非常简单的答案。尽管上述所有猜想都有其优点,但这是一个很好的观点。看起来可能有多个解决方案是正确的,所以他们只选择了最有可能使用的一个......也许? 我想在语法中有一个额外的元素会很好,这样你就可以写padding: n [type]
。因此,您将拥有 padding: 5% logical
(OP 建议的内容),而不是 padding: 5% width
,第二个参数默认为“宽度”以实现向后兼容性。
“n% 边距(和填充)不会在所有四个方面都表示相同的意思” - 但为什么会出现问题呢?【参考方案3】:
在玩了这个JSFiddle(在Chrome 46.0.2490.86上)并参考this post(用中文写)之后,我投票给@ChuckKollars的答案。
反对无限计算猜想的一个主要原因是:使用width
面临同样的无限计算问题。
看看这个JSFiddle,parent
显示的是inline-block
,可以在上面定义边距/填充。 child
的保证金值为 20%
。如果我们遵循无限计算猜想:
child
的宽度取决于parent
parent
的宽度取决于child
但结果,Chrome 在某处停止了计算,结果是:
如果您尝试在 JSFiddle 上水平展开“结果”面板,您会发现它们的宽度不会改变。请注意child
中的内容被包装成两行(不是,比如说,一行),为什么?我猜 Chrome 只是在某个地方硬编码它。如果你编辑child
的内容使其更多(JSFiddle),你会发现只要横向有多余的空间,Chrome就会将内容保留两行。
所以我们可以看到:有一些方法可以防止无限计算。
我同意这样的猜想:这种设计只是为了保持四个边距/填充值基于相同的度量。
this post(中文写)也提出另一个原因是:是因为阅读/排版的方向。我们从上到下阅读,宽度固定,高度无限(实际上)。
【讨论】:
这是因为网页在通常无限的方向上垂直滚动;所以宽度可以从浏览器窗口的宽度计算出来。元素比屏幕宽的唯一方法是,如果您指定它们的宽度更大。典型的块元素宽度为 100%,并在垂直方向自动扩展(未知)。这就是为什么宽度没有同样的问题。【参考方案4】:我意识到 OP 在问 为什么 CSS 规范将顶部/底部边距百分比定义为宽度的百分比(而不是假设的高度),但我认为它也可能是有用的发布一个潜在的解决方案。
现在大多数现代浏览器都支持 vw 和 vh ,这使您可以根据视口宽度和视口高度指定边距数字。
如果没有滚动条,100vw/100vh 分别等于 100% 宽度/100% 高度;如果有滚动条,则视口数字不考虑这一点(而 % 数字可以)。值得庆幸的是,几乎所有浏览器都使用 17 像素大小的滚动条 (see here),因此您可以使用 css calc 函数来解决这个问题。如果你不知道滚动条是否会出现,那么这个解决方案是行不通的。
例如:假设没有水平滚动条,50% 高度的上边距可以定义为“margin-top: 50vh;”。使用水平滚动条,这可以定义为“margin-top: calc(0.5 * (100vh - 17px));” (请记住,calc 中的减号和加号运算符两边都需要空格!)。
【讨论】:
在许多情况下这是一个有用的解决方法,但是有很多例子表明它不起作用(例如,如果你试图使内容与 flexbox 容器的大小成比例地增长到填充可用空间,同时还有另一个内容可变的框)。【参考方案5】:我知道这个问题有点老了,但我想为 CSS3 刷新它。虽然 CSS2.1 规范确实说百分比填充和边距是相对于包含块的宽度定义的,但情况并非总是。这取决于写作模式。这直接来自CSS3 specs:
因此,margin 和 padding 属性的百分比(在 CSS2.1 中总是根据包含块的宽度计算),在 CSS3 中是根据包含块的内联大小计算的。
我在我的tutorial on aspect ratios with CSS 中介绍了这一点。
具体来说,有一个关于Percentage Padding in Horizontal vs. Vertical Writing Modes 的部分。默认情况下,元素具有水平书写模式,其中文本从左到右水平流动(在“内联”方向)。但是,使用writing-mode
CSS 属性,您实际上可以将模式设置为垂直(文本从右到左或从左到右流动)。下面是一些横写模式和竖写模式的图表:
这些来自MDN docs on writing modes。
在垂直书写模式下,百分比填充将相对于包含块的高度,而不是宽度。
这是证据:
.document
writing-mode: vertical-rl;
width: 100%;
height: 100vh;
.parent
width: 100%;
height: 200px;
background-color: black;
color: white;
.child
padding: 10%;
background-color: white;
color: black;
border: solid 1px;
<div class="document">
<div class="parent">
<div class="child">
Child
</div>
</div>
</div>
孩子获得 20px 的内边距,这是其包含块高度 (200px) 的 10%。
至于问题中的原因,这里的其他帖子对此进行了很好的介绍。
【讨论】:
以上是关于为啥 CSS 中的边距/填充百分比总是根据宽度计算?的主要内容,如果未能解决你的问题,请参考以下文章
如何根据自己的宽度而不是父 div 将 CSS 边距/填充设置为子 div?