margin-top和padding-top设置百分比问题
我们都知道css很简单。所以在平时的开发当中,也没有在意一些具体的细节。我们知道某一个属性值大概的含义。比如我们知道css的盒模型,是由margin, border, padding, content 四个部分组成。如果我们想要设置一个元素的外边距,只需要设置margin值就可以。
margin取值可以为数值、百分比以及auto。margin为简写的形式,我们还可以分别设置margin的top、right、bottom、left值。比如我们写如下的代码:
<style>
.margin-container {
background: #333;
width: 400px;
height: 200px;
color: #fff;
}
.margin-child {
margin-top: 10%;
width: 200px;
height: 100px;
background-color: black;
}
</style>
<div class="margin-container">
<div class="margin-child">
margin content
</div>
</div>
那么问题来了,其中margin-top: 10%;
这个百分比,究竟是基于谁来计算的百分比呢?
再来看一个例子:
<style>
.padding-container {
background: #333;
width: 400px;
height: 200px;
color: #fff;
}
.padding-child {
padding-top: 20%;
width: 200px;
height: 100px;
background-color: black;
}
</style>
<div class="padding-container">
<div class="padding-child">
padding content
</div>
</div>
此处的padding-top: 20%;
又是基于谁来计算的百分比呢?
如果你已经清楚答案了, 那可能没有再继续阅读下去的必要。但是如果你还有疑问的话,那我们接下来就一探究竟。
结果分析
我们先不看结果,按照常规的思维来想一想。页面当中有一个.margin-container
容器元素,一个.margin-child
子元素。容器元素以及子元素都设置了相应的宽高。我们都知道块级元素在页面中是按照垂直方向挨个排列的,而margin-top是当前设置元素的外上边距,所以应该是在垂直方向上设置的一个距离。设置为百分比的话应该是根据元素的高度来进行相应的计算的。我们暂且先不管对错。
此处有两个问题:
- margin-top设置百分比时是基于自身元素还是容器元素的值来计算的?
- margin-top设置百分比时是基于height,还是width计算的?
接下来然我们看看具体的结果:
其实我们从这个图当中就能获取到我们想要的答案。
首先我们看margin-child的margin-top值设置为10%。在chrome开发者工具中,我们能看到其margin-top计算后的值为40px。而我们的margin-child宽高分别为200px、100px,margin-container宽高分别为400px、200px。所以我们可以得出结论margin-child所对应的margin-top为10%,是其父元素margin-container宽度400px的百分之十。所以以上两个问题对应的答案为:
- margin-top设置百分比时是基于容器元素的值来计算的
- margin-top设置百分比时是基于width计算的
当然,如果你再看看padding-top的设置。也会得到相同的结果。为什么?为什么会是基于宽度而非高度来计算?为什么是基于父元素而非子元素来进行计算?(此处另外如果大家注意观察的话,我们设置的是margin-child的margin-top为10%,我直觉上应该是会在margin-child与margin-container之间会有10%的距离,但是从渲染结果看,其实是margin-container与body的距离为10%,此处涉及margin合并的问题。如果不清楚可以自行查阅相关问题。
)
我们从W3C规范文档中可以知道:
规范当中就是这么规定的,也就是浏览器或者说浏览器的渲染引擎(注:也许这么叫不是很恰当)就是这么来实现的。那为什么要这么规定?基于高度计算会有什么问题?
这篇文章给出了相应的一些说明,总结出来两点:
- margin/padding计算都基于一个值,width进行计算,这样能够保证top,right,bottom,left四个值的一致性
- 由于父元素的高度是根据其所包含的子元素进行计算的,如果子元素的margin/padding是基于父元素高度计算的话,那么会引起高度计算的循环依赖。
我们着重说一说第二点。
假设我们在一开始的示例中,没有给margin-container设置宽高
.margin-container {
background: #333;
color: #fff;
}
<div class="margin-container">
<div class="margin-child">
margin content
</div>
</div>
此时父元素的宽为714px,高度为100px。因为目前只有一个子元素,子元素高度为100px,子元素的margin-top为父元素宽度的10%,计算后为71.4px。由于会存在误差所以渲染出来显示71.391px,并且子元素的margin与父元素的margin进行了合并,所以看起来是父元素距离body顶部71.391px。所以父元素的高度为100px。
试想如果我们按照高度来计算margin百分比的话,此时margin-child的margin-top值应该为100px*10%=10px。看起来似乎没有问题,但是,我们渲染的时候知道body宽度,由于没有设置margin-container的宽高,在渲染margin-contaner的时候需要计算它的宽高,此时我们可以确定其宽度为body宽度714px。高度未知,高度需要根据子元素的高度来进行计算,所以紧接着去进行margin-child的解析计算,发现child的高度为100px,margin-top为父元素高度的10%,而此时父元素高度又不确定,所以margin-child的margin-top应该是多少呢?
那有的人可能会说,父元素高度不是根据子元素高度计算么?子元素的高度为100px,所以父元素高度为100px,所以子元素的margin-top值为10px。如果浏览器引擎在解析css的时候是按照 解析子元素高度 -> 计算父元素高度 -> 计算子元素margin
这样的顺序的话,好像也是可以说的通的。
那我们再看看下面的例子:
.margin-container {
background: #333;
color: #fff;
}
.margin-child {
margin-top: 10%;
width: 200px;
height: 100px;
background-color: black;
}
<div class="margin-container">
<div class="margin-child">
margin content
</div>
<div class="margin-child">
margin content 2
</div>
</div>
运行结果为:
此时的父元素包含了两个子元素。父元素的高度计算为子第一个元素的高度+第二个子元素高度+第二个子元素的margin-top
。
那按照刚才的说法,如果浏览器引擎在解析css的时候是按照 解析子元素高度 -> 计算父元素高度 -> 计算子元素margin
这样的顺序的话,子元素高度和200px -> 父元素高度200px -> 子元素margin-top为10%=20px
。再结合刚才运行的结果,父元素的高度计算为子第一个元素的高度+第二个子元素高度+第二个子元素的margin-top
,此时父元素高度又发生了改变,那子元素的margin值还需要改变吗? 如果还需要改变的话,父元素的高度又会发生变化,此时就行程的循环依赖。
所以,你真的弄懂css中的margin和padding了吗?
以上为个人的浅见,其实有一些不严谨的地方,浏览器针对css解析以及渲染的顺序流程等,这儿权当是抛出了问题,不对的地方还望指出改正,一起探讨学习。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。