CSS属性探秘之margin
1. margin概述
1.1 定义
Margins in CSS serve to add both horizontal and vertical space between boxes. –W3C
翻译:CSS中的margin用来添加盒子之间的水平和垂直间隙。
margin
同时也被成为 外边距。按照它的W3C定义,很明了的知道,它主要是用来限定盒子边框与其他相邻盒子边框之间的距离。如同马路上汽车与汽车之间的安全距离一样,但是CSS中的外边距可比汽车之间的安全距离自由多了,可以零距离和负距离,你汽车来个负距离试试。
margin是个复合属性,属性简写设置所有四个方向的外边距,而其他外边距属性只设置其各自方向的宽度。这些属性应用于所有元素,但垂直外边距在非替代行内元素上不起作用。
1.2 margin与盒模型的关系
如下图:
无论是哪种盒模型,border
和 margin
的值,都不参与盒模型尺寸的计算。如其定义所说,它只是用来添加盒子之间的水平和垂直间隙。
1.3 margin与排版页面
margin
的设置,会影响页面排版。
把参与页面布局的盒子想象成一个个国家,一旦一个盒子声明了外边距,就是意味着,从外边距的边界开始,里面的领土都是其私有领地,任何不经过通过同意都不能进入它的领地与领空。如果它设置负边距,向上或者向左移动,旁边的盒子里面会占领它下边或者右边的空间(文档流的特性,尽量填充满整个页面)。
这个特性注意需要和相对定位区别开来。
因为正常文档元素通过设置margin属性,既有文档流的流动性,又没有脱离正常文档流,还可以用于定位,参与页面的正常排版,所有经常会用来实现一些页面布局。后面会有介绍。
2. 属性值的设置
由于 margin
是一个复合属性,与 border
和 padding
一样,由四个值组成,方向是:上→右→下→左。分别是:
margin: margin-top margin-right margin-bottom margin-left;
其它略写方式,如设置为三个值,两个值,一个值的情况,这里就不做赘述。
每个 margin
方向上可设置值如下表:
值 | 描述 |
---|---|
auto | 浏览器计算外边距。 |
length | 规定以具体单位计的外边距值,比如像素、厘米等。默认值是 0px。 |
% | 规定基于父元素的宽度的百分比的外边距。 |
inherit | 规定应该从父元素继承外边距。 |
2.1 值为auto时的表现
常用的就是让一个块级元素水平居中:margin:0 auto;
,这句代码确实有效,还十分实用,但是有人就是爱较真,会忍不住k可能会问出下面两个问题:
- 为什么
auto
能让块级元素水平居中?- 为什么通过margin不能实现块级元素垂直居中?
在W3C的文档关于块级非置换元素的宽度计算有如下规定:
此时,包含块宽度=
margin-left
+border-left-width
+padding-left
+width
+padding-right
+border-right-width
+margin-right
。(关于包含块的概念,这里暂时不做解释,以后会有文章专门介绍)。盒子的宽度计算时,如果有一个值设置为auto
,则该使用值由等式求出。
- 若 width 设置 auto, 则其他 auto 值变成 0 且 width值由等式求出。
- 若 margin-left 与 margin-right 都是 auto, 则他们的使用值相等。这会将元素水中居中在包含块中。
由于块级元素(基本上所有的块级元素都是块级非置换元素),所有它的包含块会占据整行。在通过上面第2条,就很容易得出结论。
另外,如果只是设置 margin-left: auto;
会怎样呢? 这个疑问大家可以自己试试。
从上面得出的结论是:正常文档流中的块级元素,一旦width或者水平margin值设置为auto,那意味着尽可能的占据。
关于第二个问题,想通过margin的auto来实现垂直居中,这个就不好意思了,肯定是行不通的。原因也很简单:标准规定:垂直方向上的外边距计算,都是采取保守策略,尽量采取最小值,如果是auto,则当作0。
2.2 值为百分比时的表现
先看一个例子:
<style type="text/css">
.demo-container {
width:1000px;
height: 600px;
background: #f2f3f5;
}
.demo {
margin: 10% 5%;
background-color: pink;
}
</style>
<body>
<div class="demo-container">
<div class="demo"></div>
</div>
</body>
大伙猜猜,div.demo的margin计算值为多少?
A: 计算值为
margin: 100px 30px;
!Q: 确定么?
A: 确定!
Q: 恭喜你答错了!正确答案是:
margin: 100px 50px;
,没想到吧A: 不可能,你丫忽悠我吧,给我解释解释那个垂直方向外边距的50px从哪来的
Q: 简单呀, 1000*5%
A: …
我猜很多人都猜不到这个结果,怎么可以高度和宽度的百分比都参照包含块的宽度进行计算呢?
关于值设置为%时的表现,W3C上只有一句:
百分比按照生成框的包含块的宽度计算。
个人理解,在一个复合属性中,如果可以几个值都可以设置百分比,其百分比的参照肯定一致。这是算是方便计算吧。
如果想参照包含块的高度行不行呢? 答案是肯定的,行!只要CSS中设置如下:
.demo{
-webkit-writing-mode: vertical-rl;
writing-mode: tb-rl;
}
没想到吧,原来都是书写模式搞的鬼!
3. 折叠外边距
3.1 什么是折叠外边距?
代码:
.demo1 { margin: 10px 0;}
.demo2 { margin: 20px 0;}
.demo3 { margin: 30px 0;}
看到上面结果,是不是有点奇怪,为啥第一个Div和第二个Div之间的外边距只有20px,而不是30px。原因是,这里垂直方向上的外边距发生了折叠。
在CSS中,两个以上的框(可能是兄弟,也可能不是)之间的相邻外边距可以被合并成一个单独的外边距。通过此方式合并的外边距被称为折叠,且产生的已合并的外边距被称为折叠外边距。
相邻垂直外边距合并,除了:
- 根元素的框的外边距不合并。
- 如果一个有间隙的元素的上、下外边距相邻,则其外边距将与下面的兄弟的外边距合并,但产生的边距不会与父亲区块的下外边距合并。
*水平外边距不会合并。(其实和书写模式有相关!)*。
3.2 什么情况下会产生折叠外边距
产生规则:处于同一个块级上下文(BFC)中的块元素,没有行框
、没有间隙
、没有内边距
和边框
隔开它们,这样的元素垂直边缘毗邻,则称之为相邻。
通过上面规则介绍可知,产生折叠外边距必要有个三个条件:都是同一个BFC中的块元素、没被隔开、垂直边缘毗邻。
那什么是垂直边缘毗邻?
- 元素的上外边距和其属于常规流中的第一个孩子的上外边距。
- 元素的下外边距和其属于常规流中的下一个兄弟的上外边距。
- 属于常规流中的最后一个孩子的下外边距和其父亲的下外边距,如果其父亲的高度计算值为 auto。
- 元素的上、下外边距,如果该元素没有建立新的块级格式上下文,且 min-height 的计算值为零、height 的计算值为零或 auto、且没有属于常规流中的孩子。
3.3 如何避免折叠外边距
因为产生折叠外边距的初衷是用来文字排版,但是目前却经常发生在页面布局中,这反而有点出人意料,折叠外边距在此时就这样被嫌弃了,需要被避免产生。
避免的方法也很简单:
- 让两个块元素处于不同BFC中
- 用间距、内边距或者边框将之隔开
4. margin在布局中妙用
4.1 经典的圣杯布局
圣杯布局是指左右两栏固定宽度,中间部分自适应的一种常见布局,如下图:
主要代码
- css部分:
.demo-container {
overflow: hidden; /* 清除浮动 */
padding: 0 200px;
}
.left-nav, .right-nav, .main-wrap {
float: left;
height: 300px;
}
.main-wrap {
width: 100%;
}
.left-nav, .right-nav {
position: relative;
background-color: pink;
width: 200px;
}
.left-nav {
margin-left: -100%;
left: -200px;
}
.right-nav {
margin-left: -200px;
right: -200px;
}
- html部分:
<div class="demo-container">
<div class="main-wrap">main</div>
<div class="left-nav">left</div>
<div class="right-nav">right</div>
</div>
核心思想:利用负外边距和相对定位相互抵消来实现。
4.2 单列变三列(负外边距的妙用)
把单个列表变成三列:
实现超简单:
- html部分
<ul>
<li class="col1">Eggs</li>
<li class="col1">Ham<li>
<li class="col2 top">Bread<li>
<li class="col2">Butter<li>
<li class="col3 top">Flour<li>
<li class="col3">Cream</li>
</ul>
- css部分
ul {list-style:none;}
li {line-height:1.3em;}
.col2 {margin-left:100px;}
.col3 {margin-left:200px;}
.top {margin-top:-2.6em;} /* the clincher */
通过对.top的添加margin-top:-2.6em。所有的元素会完美的对齐好。使用负边距会比使用相对定位好很多,因为你只需要给新的一列的第一个元素添加负边距即可。
4.3 其他应用
margin的其他使用,主要还是利用负外边距与float、内联框等,实现水平上的布局。
5.总结
之前从未想过,简单如margin这样的属性,仔细研究起来,居然也有这么多知识点。 这个周末过的很颓废,窝在家看看电视剧,就过了…
参考:
[负边距详解][9]