首先要明确以下两点:
1、元素的显示区域是指:border_left + border_right + paddiing_left + paddiing_right + content ;不包括margion;
2、margin只是是元素盒子的外边距。用于处理元素之间间隔的属性。
详见:CSS——盒子模型
一、基准线的概念:
基准线或者称为参照线,就像物理中学习的相对运动,物体是向左运动、向右运动亦或是静止,取决于参照物。比如对A、B两个物体进行如下判断:
a、以A物体为参照物,判断物体B的运动方向、距离;
b、以B物体为参照物,判断物体A的运动方向、距离;
在CSS中,我们也可以虚构出基准线这样一个概念。所不同的是,在物理中我们可以随时改变参照物;但是在CSS的具体的情况下,基准线却是不可更改的。
众所周知margin包括:margin-top 、margin-right、margin-bottom、margin-left四个,这四个外边距的基准线可以分为两大类:
- 以外物作为基准线的:margin-top、margin-left,影响的是自己的显示位置;
- 以自身的border为基准线的:margin-right、margin-bottom,影响的是后面兄弟元素的显示位置;
兄弟元素之间的基准线选择:
首先定义一个概念:元素的显示区域边界。
显示区域的左边界按照以下原则的先后顺序进行确认:
a、有 border_left,则以 border_left 的最左边为显示区域的左边界;
b、有 paddiing_left, 则以 paddiing_left的最左边为显示区域的左边界;
c、以content的最左边为显示区域的左边界;
显示区域的上边界、右边界、下边界与此类似,不在赘述。
明确显示区域的边界之后再来看基准线:
1、margin-top的外物基准线的选择:
a、垂直上面的兄弟元素显示区域的下边界位置; ???????是没有脱离文档流还是相连时
b、垂直上面无兄弟元素,并且父亲元素有border-top或者padding-top,则为父元素的 content 的上边位置;
c、父元素不符合条件,则以父元素垂直上面的兄弟元素的显示区域的下边界为基准线;
d、依次类推;
2、margin-left的外物基准线的选择:
a、水平左边的兄弟元素显示区域的右边界位置;
b、水平左边无兄弟元素,则为父元素的 content 的左边位置;
c、如果父元素没有
3、margin-right的自身基准线:以自身显示区域的右边界为基准线;
4、margin-bottom的自身基准线:以自身显示区域的下边界为基准线;
例子如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <style type="text/css"> .back{ background-color:greenyellow; } .margin{ margin: 20px; border-color:red ; } .border{ border: solid red 2px; } </style> </head> <body> <div id="div_1" class="back border" style="height: 50px;"> div_1 </div> <div id="div_2" class="back"> <div id="div_2_1" class="back margin border" > 我是子元素A </div> <div id="div_2_2" class="back margin border" > 我是兄弟元素B </div> <div id="div_2_3" class="back border" > 我是兄弟元素C </div> </div> </body> </html>
效果如下:
1、两块黄绿色之间的白色区域的成因:因为元素A(div_2_1)的父元素div_2 即没有border,也没有padding,导致元素A的基准线变成了div_1。所以元素A与div_1之间有了间隔,而div_2 没有将这段间隔纳入显示区域;
2、为div_2 增加 border 样式,两块黄绿色之间的白色区域会消失:元素A的基准线变成了父元素div_2。间隔的位置存在div_2的border内部,也就是在div_2的显示区域内。而margion是透明的,显示div_2的背景颜色,所以白色消失。
3、元素C没有设置 margion,但是元素B 的margion:20px,包含了margion-bottom:20px,所以与兄弟元素B之间有间隔;
二、margin垂直外边距合并:
在垂直方向上都有外边距margion的时候,两个margion会合并,并取最大的为两者之间的间隔:
将上面例子中的元素C 设置style属性如下:
<div id="div_2_3" class="back border" style="margin-top: 40px;" > 我是兄弟元素C </div>
会发现元素B、C之间的间隔变成了40px,而不是60px;
三、margin设置值的正负问题:
对于margion值的正负,表示的意义是截然不同的。以下均以20px大小为例。?????代表了盒子相对于基准线的移动方向:
1、margin-top:
正:自基准线起,向下取20px作为margin-top,盒子位置随即向下移动20px;
负:首先基准线向上移动20px;在新的基准线起向上取20px作为margion-top的区域;盒子位置向上移动20px;
2、margin-left: 正:向右;负:向左;
正:自基准线起,向右取20px作为margin-left,盒子位置随即向右移动20px;
负:首先基准线向左移动20px;在新的基准线起向左取20px作为margin-left的区域;盒子位置向左移动20px;
3、margin-bottom:正:向上;负:向下;
正:自基准线起,向上取20px作为margin-bottom,盒子高度随即向上增大20px;
负:首先基准线向下移动20px;在新的基准线起向下取20px作为margin-bottom的区域;盒子高度随之向下增大20px;
负值新解释:
margion是用于元素之间保持一定间隔的属性。
4、margin-right:正:向左;负:向右;
正:自基准线起,向左取20px作为margin-right,盒子宽度随之向左增大20px;
负:首先基准线向右移动20px;在新的基准线起向右取20px作为margin-right的区域;盒子宽度随之向右增大20px;
四、margin对不同元素的作用效果:
1、对于块级元素可以任意设置,均有效果;
2、对于可替换元素如:select、input、object、img、button、label、textarea等元素,由于其基本类似于块级元素,因此也可以设置;
3、对于非可替换内联(行内)元素,只有 margin-left/margin-right 能够起到一定效果。
详细分析
下面引入margin的“参考线”的概念,margin的“参考线”W3C中没有严格的规范,但通过我们的实战可以很轻易的总结出一些规律,怿飞的总结,怿飞也对margin做了详细的探究(但有部分观点本人不作认同):
如上图是一个盒模型(略去内边距),虚线所示即为margin的参考线,讲作margin 移动的基准点可能更好理解,实线所示为border,浅绿色纯色为margin的区域,显然我们可以把margin的参考线分为两类,top 和 left 的参考线属于一类,right 和bottom 的参考线属于另一类,那它们到底各以什么为参考线呢:
- top 以 containing block 的 content 上边或者垂直上方相连元素 margin 的下边为参考线垂直向下位移;left 以 containing block 的 content 左边或者水平左方相连元素 margin 的右边为参考线水平向右位移。
- right 以元素本身的 border 右边为参考线水平向右位移;bottom 以元素本身的border 下边为参考线垂直向下位移。
前面的DEMO1中A的margin-right为-50px,按照“right 以元素本身的 border 右边为参考线水平向右位移”的解释,此时A应该以元素本身的border右边向左位移50px才对(负值与正值的位移方向相反),但要注意!上面讲的位移并不是元素本身的位移,而是自身margin四个方向(top ightottomleft)的具体位置的位移! 而实际上虽然A未发生位移,但B却向左位移了50px,这又是为何?B的margin默认为0,B的margin-left寻找的左边参考线即为A元素的margin-right的具体位置,而此时A的margin-right的具体位置在哪?见下图:
按照“right 以元素本身的 border 右边为参考线水平向右位移”的规则,这时的margin的right应从图示蓝色线开始位移,如果是正值则向右位移,但现在是负值,于是向左位移,最终绿色的线为A元素margin的right最终的位置,这个位置也将作为它右边紧跟的B元素margin-left的参考线,而DEMO1中B并未设置margin,即margin默认为0,很显然,B元素就出现在绿色线之后,而覆盖上A元素之上则是由于DOM中后面元素比前面元素优先级高的缘故(当然你也可以为A设置”position:relative”而使A覆盖在B之上)。
来看下DEMO2,A并未设置margin则margin默认为0,B设置了margin-left为-50px,B的margin-left是以A的margin的right的位置为参考线,此时A的margin为0,则B的margin-left即以A的右border为参考线开始位移,如果是正值则是向右位移,现在是负值即向左位移,所以最终也会和DEMO1一样呈现出一样的现象:B覆盖于A之上,位移大小为50px。
DEMO3中的关系其实是父子元素的关系,根据前面的“left 以 containing block 的 content 左边向右位移”的规则,A设置了margin-left为-50px,本是正值向右位移,现在是负值,则向左位移50px,参考线是包含块的content左侧,来看下图:
按照“left 以 containing block 的 content 左边向右位移”的规则,这时A的margin的left应从图示蓝色线开始位移,如果是正值则向右位移,但现在是负值,于是向左位移,最终绿 色的线为A元素margin的left最终的位置。
DEMO4其实和DEMO3是一个性质,由于是父元素本身margin的left发生位移,那么它就会依据DEMO3一样继续去找它的包含块的content左侧,发生了左移也是很好理解的,至于其中的A只是混淆视听罢了,在父元素中,A其实是相对于父元素静止的。
通过四个DEMO的分析,并加入参考线的概念后大家是否可以理解了呢?不知道各位有没有这样一个疑问:margin-left的时候元素本身发生了位移,但margin-right为何并没有致使本身发生位移?下面谈谈我个人的理解:
元素之间有个例如DEMO1中的A元素设置了margin-right为-50px,按道理讲应当向右位移50px,但是根据页面排版顺序来讲,必须先渲染左边的元素,渲染完了再渲染右边的元素,那么这时右边元素的位置则依赖于左边的元素,左边元素的margin-right不可能先去找右边元素的参考线,左边元素的margin-right的作用仅仅是提供了右边元素margin-left的参考线。而如果是父子元素的关系,则最左边的子元素的margin-left则依赖于它的包含块。注意了,这里有一个特殊情况,当为元素设置了”float:right”时,元素会脱离布局流,这时如果再为它设定marign-right为-50px时,它本身会发生偏移,因为它依赖于它的包含块。这与怿飞总结的“如果是负的 top 或 left 值会引起 box 的向上或向左位置移动,如果是 bottom 或 right 只会影响下面 box 的显示的参考线”的也是有出入的。
http://www.hicss.net/i-know-you-do-not-know-the-negative-margin/
http://www.w3school.com.cn/css/css_margin_collapsing.asp
http://www.planabc.net/2007/03/18/css_attribute_margin/
http://www.smallni.com/negative-margin/
http://www.zhihu.com/question/20116963
http://www.hicss.net/do-not-tell-me-you-understand-margin/
http://www.hicss.net/separation-of-powers-model-in-css-design-patterns/
http://www.cnblogs.com/dolphinX/p/4071725.html