CSS-深入理解之margin
版权声明:本文为博主原创文章,未经博主允许不得转载。
在一次面试过程中,面试官问到margin的塌陷的问题,以及margin负值的问题。当时有点蒙,对这块知识点的理解一点都不深入。为了补充这方面的知识,特意去查了一下相关的资料,发现张鑫旭大神在慕课网上有视频讲解。在这里,梳理和总结一下其中的知识点。
1. margin可以改变容器的尺寸
1.1 margin能够改变可视区域的尺寸的条件
- 适用于没有设定width/height的普通block水平的元素(float元素,absolute/flex元素,inline水平,table-cell元素)
- 只适用于水平方向的尺寸
1.2 margin能够改变元素占据的空间
- block/inline-block元素均有效
- 与有没有设置width/height无关
- 水平和垂直方向均有效
2. 百分比margin的计算规则
一般的,在CSS中,尺寸除了使用px以外,还可以使用百分比,当使用百分比的时候,是相对于父元素的宽和高的。比如width: 10%是相对于父元素的width,
height: 10%是相对于父元素的height。
- 但是margin的话,left,top等设置百分比的时候,都是相对于第一个父元素宽度的尺寸。
- 如果是绝对定位了的话,则不一定是相对于第一个父元素的宽度的尺寸了,而是相对于第一个position为absolute,relative,fixed的父元素或者是html
3. magin重叠
3.1 margin重叠的特性
- 只发生在block水平的元素(不包含float和absolute的元素);
- 只发生在垂直方向(margin-top,margin-bottom);
3.2 margin重叠发生的三种情景
- 相邻的兄弟元素;
- 父级和第一个/最后一个子元素;
- 空的block元素;
3.3 父子margin重叠的其他条件
3.3.1 margin-top重叠
1.父元素非BFC
2.父元素没有border-top设置
3.父元素没有padding-top设置
4.父元素和第一个子元素之间没有inline元素隔开
3.3.2 margin-bottom重叠
1.父元素非BFC
2.父元素没有border-top设置
3.父元素没有padding-top设置
4.父元素和第一个子元素之间没有inline元素隔开
5.父元素没有height,min-height,max-height设置
3.4 空block元素margin重叠的其他条件
1.父元素没有border-top设置
2.父元素没有padding-top设置
3.父元素和第一个子元素之间没有inline元素隔开
4.没有height,min-height,max-height设置
3.5 margin重叠的计算规则
1.正正取大值
2.正负值相加
3.负负最负值
3.6 margin重叠的意义
- 连续段落或者列表之类,如果没有margin重叠的话,首尾项间距会和其他兄弟标签1:2关系,排版不自然。
- web中任何地方嵌套或者直接放入裸div,不会影响原来的布局。
- 遗落的任意多个空p标签,不要影响原来的阅读排版。
3.7 善用margin
做列表的时候,虽然只设置margin-top也可以达到布局的目的,但是为了健壮性,可以设置margin-top和margin-bottom,比如最后一个元素被移除或者位置被置换了,都不会破坏原来的布局样式。
4. magin: auto
4.1 margin: auto的作用机制
原本应该自动填充的尺寸被width/height强制变更,而margin: auto就是为了填充这个变更的尺寸而设计的。
4.2 如果一侧是定值,一侧是auto,auto为剩余空间的大小;
例子:
div{ width: 500px; margin-right: 50px; margin-left: auto }
- 1
- 1
此时div距离右侧的为50px,而左侧是自适应的。
4.3 如果两侧都是auto,则会让元素水平居中
4.4 margin: auto只对block元素有效,对于inline和inline-block水平的元素。并且只对水平方向有效而对垂直方向是无效的,因为水平方向会自动填充至父元素的宽度大小,而垂直方向不会。
4.5 父元素设置writing-mode: vertical-lr,子元素设置margin: auto,子元素会垂直居中,但是不会水平居中。
.father { wrting-mode: vertical-lr; }
.son {margin: auto}
- 1
- 2
- 1
- 2
4.6 absolute与margin垂直居中
.father { width: 100px; height: 100px; position: relative; }
.son { width: 50px; height: 50px; position: absolute; left: 0; top: 0; bottom: 0; right: 0; margin: auto; }
- 1
- 2
- 1
- 2
这种方式适用于IE8+。这种方式之所以可以水平垂直居中,是因为绝对定位的元素,如果left, right, top, bottom都设置为0的话,是自动拉伸到父元素的宽和高的100%,当对子元素设置了宽和高之后,这时候auto就可以起作用了。
5. margin负值定位的实例
5.1 margin负值下的两端对齐
这主要是用了margin改变元素尺寸大小的原理来实现的。
比如说有一个两端对齐的列表需要实现,正常情况下是这么布局的。
HTML
<div class="box">
<div class="ul">
<div class="li">列表1</div>
<div class="li">列表2</div>
<div class="li">列表3</div>
</div>
</div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
CSS
.box {
width: 660px;
height: 200px;
background-color: yellow;
}
.ul {
overflow: hidden;
}
.li {
width: 200px;
height: 200px;
margin-right: 20px;
background-color: green;
float: left;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
这样出来的效果是,右侧有一个margin-right的20px,不太好去除掉。
这时候可以给div.ul设置一个margin-left=-20px,将div.ul扩大到680px宽度。这时候再次计算一下每一个li应该设置的宽度就可以了。布局如下:
HTML
<div class="box">
<div class="ul">
<div class="li">列表1</div>
<div class="li">列表2</div>
<div class="li">列表3</div>
</div>
</div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
CSS
.box {
width: 660px;
height: 200px;
background-color: yellow;
}
.ul {
overflow: hidden;
margin-right: -20px;
}
.li {
width: 206.66px;
height: 200px;
margin-right: 20px;
background-color: green;
float: left;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
5.2 margin负值下的两端对齐
这种布局的原理是利用margin改变元素占据空间来实现的。
比如有两列布局,需要实现的是,任意一列布局的高度改变后,另外一列的高度也跟着变化,实现二者是等高的。正常的布局如下:
HTML
<div class="box">
<div class="child-orange">左侧</div>
<div class="child-green">右侧</div>
</div>
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
CSS
.box {
width: 400px;
overflow: hidden;
}
.child-orange {
width: 200px;
height: 100px;
background-color: orange;
float: left;
}
.child-green {
width: 200px;
height: 100px;
background-color: green;
float: left;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
当改变左侧div的高度为200px的时候,则变为这样了:
这样的话,左右两侧的高度就不是等高了。为了实现等高的效果,则需要给这两个div设置margin-bottom负值,同时设置padding-bottom负值来让元素的实际高度显示出来。(ps:此处不明白是什么意思)
CSS
.box {
width: 400px;
overflow: hidden;
}
.child-orange {
width: 200px;
height: 100px;
background-color: orange;
float: left;
}
.child-green {
width: 200px;
height: 100px;
background-color: green;
float: left;
}
.child-orange, .child-green {
margin-bottom: -500px;
padding-bottom: 500px;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
可见,利用这种方法,就实现了等高的效果。这种方法兼容性可以,支持IE6+。但是有其副作用,会在overflow: hidden的文章里面细讲。
6. margin失效的情景
6.1 inline水平元素的垂直margin无效
这里面有两个前提:
- 非替换元素,比如不是 img 元素
- 正常书写模式,不是writing-mode: vertical-*
6.2 display: table-cell的元素margin无效
6.3 绝对定位元素的非定位方向的margin“无效”
所谓非定位方向指的是left, right, top, bottom中没有设置数值的方向。比如一个图片设置了绝对定位:
img {position: absolute; left: 10px; top: 10px;}
- 1
- 1
这样的话,right和 bottom是非定位方向。设置margin-right和margin-top是“无效”的。
细心的同学可能观察到,“无效”这两个字一直都是打上了引号的,这是因为其实绝对定位的元素在各个方向的margin都是有效的,只是因为绝对定位的元素脱离了文档流,所以跟普通文档流中的元素不同,其margin对相邻元素的布局没有影响,所以看起来是无效的,但是如果margin足够大时,触碰到了所依靠定位的父元素的边缘,则margin的作用就显现出来了。