CSS代码:
body
{
background-color:yellow;
}
.par {
margin-top:10px;
background-color:red;
width:100px;
height:100px;
}
HTML部分:
javascript部分:
window.οnlοad=function()
{
/*记住,通过window.getComputedStyle要通过window.onload以后才能完成,或DOMContentLoaded时候触发也行*/
// var top=window.getComputedStyle(document.getElementById("par"),null)["width"];
//console.log(top);
var result=document.getElementById("par").getBoundingClientRect();
console.log(result);
}
note: getComputedStyle必须在onload或者DOMContentLoaded时候才能调用。这时候body的margin-top为8px,而par的margin-top是10px,因此会发生重叠,最后top属性为10px!left是8px,因为左右不会发生margin重叠的现象!
第一步:我们把body的css改成如下:
body
{
background-color:yellow;
border:1px solid red;
}
note:添加边框以后margin-top就不会发生重叠,因此调用getBoundingClientRect结果为top:19px,left:(8+1)px;
body
{
background-color:yellow;
padding:1px;
}
note:这时候margin也不会发生重合,getBoundingClientRect结果为top=8+1+10=19px,left=8+1=9px;
CSS部分:
body
{
background-color:yellow;
}
.par {
margin-top:10px;
background-color:red;
width:100px;
height:100px;
}
.child {
background-color:green;
width:100px;
height:100px;
margin-top:20px;
}
HTML部分:
JS部分:
window.οnlοad=function()
{
var result1=document.getElementById("child").getBoundingClientRect();
console.log(result1);
}
note:这时候会连续发生margin重叠,body的margin-top为8px,par的margin-top是10px,child的margin-top为20px,
同时都没有设置margin/padding,最后通过getBoundingClientRect的调用得出top=20px,left=8px;
注意:这时候body元素,child元素,par元素的通过getBoundingClientRect得到的top值都是20px,也就是距离文档顶部为20像素!在页面如何排列就很容易理解了!
body
{
background-color:yellow;
border:1px solid red;
}
note:这时候id="par"和id="child"的div仍然会发生重叠,导致par的margin-top变成20px,但是因为其父元素具有border,所以不会继续发生重叠。于是getBoundingClientRect的值为20+1+8=29px(par和child都是29px),但是对于body来说,
因为他的margin-top没有同后面子元素发生重合而改变,所以其值为8px!
body
{
background-color:yellow;
border:1px solid red;
}
.par {
margin-top:10px;
background-color:red;
width:100px;
height:100px;
border:1px solid gray;
}
.child {
background-color:green;
width:100px;
height:100px;
margin-top:20px;
}
note:这时候都不会发生margin-top重叠的现象,所以通过getBoundingClientRect得到的id为child的div的top=8+1+10+1+20=40px,同时id为par的top为=8+1+10=19px;同时两者的left都=8+1=9px。body的top仍然是8px!
CSS部分:
body
{
background-color:yellow;
}
.par {
background-color:red;
width:100px;
height:100px;
margin-bottom:10px;
}
.child {
background-color:green;
width:100px;
height:100px;
margin-top:20px;
}
HTML部分:
JS部分:
window.οnlοad=function()
{
var result1=document.getElementById("child").getBoundingClientRect();
console.log(result1);
}
note:这时候id为par的元素和id为child的元素会发生margin-bottom和margin-top重叠的情况,最后child的margin变成了20px,也就是距离par的距离变成了20px。然而,par和body是父子关系,同时par没有设置margin-top所以无法和body发生margin-top重合的情况,最后id为child的div通过getBoundingClientRect得到top=20+100+8=128px; left=8px;注意:这时候par的元素的getBoundingClientRect的值为8px
第二步:我们给par元素加上一个border,CSS如下:
body
{
background-color:yellow;
}
.par {
background-color:red;
width:100px;
height:100px;
margin-bottom:10px;
border:1px solid gray;
}
.child {
background-color:green;
width:100px;
height:100px;
margin-top:20px;
}
note:
这时候我们发现两个div的margin-top和margin-bottom依然发生了重叠,通过设置border/padding的方法不能解决margin-bottom/margin-top存在重叠的问题。这时候child的top为20+1+1+100+8=130px。那么如果我就是不希望两个div发生重叠呢,于是这里引入 BFC的概念,因为BFC强调:
Box
垂直方向的距离由margin决定。属于同一个
BFC
的两个相邻
Box
的margin会发生重叠。
body
{
background-color:yellow;
}
.par {
background-color:red;
width:100px;
height:100px;
margin-bottom:10px;
border:1px solid gray;
}
.child {
background-color:green;
width:100px;
height:100px;
margin-top:20px;
}
#bfc
{/*通过overflow来触发元素的BFC*/
overflow:hidden;
}
HTML如下:
note:
这时候par和child就不是同属于body构建的BFC中了,于是他们的margin就不会发生重叠!这时候getBoundingClientRect输出child的top属性为=20+1+1+10+100+8=140px
问题1:负margin对元素大小有没有影响?
情况一:margin-bottom为负值的情况(当父元素没有指定高度的时候,会修改父元素的高度!)
如果子元素没有margin-bottom:-10px,那么这时候显然父元素的offsetHeight应该是54px。但是我们给子元素一个margin-bottom,这时候父元素的边界就会往里面收缩,因此父元素offsetHeight变成了50-10+2+2=44px!
var dom1=document.querySelector("#margin");
console.log(dom1.offsetHeight);//打印44px
也就是说margin-bottom为负数会影响父元素的高度,但是其是否会影响子元素自身的高度呢?
var dom1=document.querySelector("#child");
console.log(dom1.offsetHeight);//打印52px
console.log(dom1.clientHeight);//打印50px
//负margin-bottom不会影响元素的offsetHeight,clientHeight等元素大小
//其作用只会影响文档流的边界,也就是父元素不再仅仅包裹着子元素,而是
//父元素的边界会往上面缩,也就是其父元素的边界在子元素边界上面
从上面的测试结果来看,margin-bottom为负值只会影响父元素的高度
(前提是父元素没有明确指定height),无法影响子元素自身的高度!
通过下面的分析我们知道,margin-right为负数只会改变元素自身的宽度,不会修改父元素的宽度
var dom1=document.querySelector("#child");
console.log(dom1.offsetWidth);
//打印810px,也就是说这时候子元素比父元素还要宽10px,也就是margin-right负值使得元素尺寸变化!
console.log(dom1.clientWidth);
//打印808px=(800+10-2)=808px,画一个图分析还是很容易的,本来占据了父元素的width为800px,然后有加长了10px
//但是要减去自己的border为2px!
var dom2=document.querySelector('#margin');
console.log(dom2.offsetWidth);//打印802px
console.log(dom2.clientWidth);//打印800px
但是margin-right要能够拉长元素的尺寸也是有条件的:
父元素有宽度尺寸限制(div元素)。如果没有指定父元素,这时候是无法拉长元素的
js部分如下
var dom1=document.querySelector("#child");
console.log(dom1.offsetWidth);
//打印102px,margin-right无法拉长自身!
console.log(dom1.clientWidth);
//打印100px,也就是margin-right为负值无法改变元素自身的尺寸!
margin-left为负数在这种情况下也会修改元素自身的尺寸大小
这时候元素自身的宽度受到margin-left/margin-right负值的双重拉伸作用
var dom1=document.querySelector("#child");
console.log(dom1.offsetWidth);//打印1000px
console.log(dom1.clientWidth);//打印998px
margin-bottom通过收缩文档流改变了父元素的尺寸大小,margin-right/margin-left在父元素尺寸固定的情况下可以改变子元素自身的尺寸!其它情况下都无法通过margin负值修改元素本身的大小或者父元素的大小
margin-left为负数,元素往左边移动,margin-top为负数元素往上面移动,但是移动的时候元素的尺寸是没有变化的
var dom1=document.querySelector("#margin");
console.log(dom1.offsetWidth);//打印802px
console.log(dom1.clientWidth);//打印800px
console.log(dom1.offsetHeight);//打印52
console.log(dom1.clientHeight);//打印50
margin-right为负数边界往里面收缩,为正数表示边界往外扩张,同理margin-bottom为负数表示边界往内部收缩,为正数表示往外部扩张:
这时候因为第一个div的margin-right为负数,表示元素右边边界往左边收缩了,所以后面的div元素会直接和第一个div重叠了!由浅入深漫谈margin一文中指出了:border+可视区域构成了我们的物理大小,而物理大小不随着margin负值的改变而改变(一般情况下是这样,因为clientWidth+border是不包含margin部分的,但是除了上面这种情况二);同时该文也提出了逻辑大小的概念,元素的逻辑大小会影响后续元素的显示,负数的margin-bottom会导致元素的边界收缩,从而后面的元素会占据收缩产生的那一部分文档流,从而形成后面元素覆盖前面元素的效果(从后面可以看出,后面元素的客户区无法覆盖前面元素的border);负数的margin-right也是一样的道理!但是,对于margin-left除了上面这种特殊情况可以用于改变元素的大小以外,其它情况都是导致元素向左移动;负数margin-top只会导致元素向上面移动;负数margin-right除了上面这种特殊情况可以修改元素的宽度以外都是导致元素边界收缩,使得后面的元素可以覆盖元素本身!该文给出了结论:
box 最后的显示大小等于 box 的 border 及 border 内的大小加上正的 margin 值。而负的 margin 值不会影响 box 的实际大小,如果是负的 top 或 left 值会引起 box 的向上或向左位置移动,如果是 bottom 或 right 只会影响下面 box 的显示的参考线。其中除了“而负的 margin 值不会影响 box 的实际大小”以外,我都是认同的,因为从上面的情况2来说,当父元素指定了宽度,但是子元素通过margin-left/margin-right负值却改变了其自身的元素大小!
解答:参见这篇文章可以知道margin-right为负值可以使得元素的子元素的宽度增加(和同时设置一个比父元素更宽的宽度是一样的效果);父元素设置了overflow:hidden有两个作用,作用一在于内部元素是浮动,overflow可以触发BFC,BFC计算高度的时候浮动元素也包含在里面,所以不会高度塌陷,作用二在于子元素宽度增加使得子元素可以存放所有的li元素,但是会有一部分超出了父元素的边界,所以这一部分内容要超出隐藏掉!这也是多列等高布局的原理所在!
- 子元素1
- 子元素2
- 子元素3
- 子元素4
- 子元素5
- 子元素6
CSS布局如下:
*{
margin:0;
padding:0;
}
#test
{
/*一排存放3个li元素所需要的宽度是330px,但是这里只有320px*/
width:320px;
border:1px solid red;
margin:0 auto;
height:210px;
}
ul
{
overflow:hidden;
/*触发父元素的BFC,因为在计算BFC高度的时候父元素也包含在里面,
同时overflow:hidden使得超过test的宽度可以隐藏!*/
margin-right:-10px;/*和直接设置为width:330px效果一样*/
}
/*每一个li元素的宽度是110px*/
li
{
width:100px;
margin-right:10px;
list-style:none;
background-color:#ccc;
height:100px;
float:left;
margin-bottom:10px;
}
其实这里的负数margin的效果和设置width是完全一样的!
该例子会导致后面的div元素的绿色覆盖上一个元素的红色的底边框,同时文档流也减少了一个像素(注意:这里有背景色,如果没有背景色无法产生覆盖的效果)
同理,我们看看如何去除最后一个li的border-bottom,导致最后一个li元素的border-bottom不和父元素的border重合:
- Test
- Test
- Test
- Test
- Test
CSS部分:
body,ul,li{margin:0;padding:0;}
ul,li{list-style:none;}
#test{
margin:20px;
width:390px;
background:#F4F8FC;
border-radius:3px;
border:2px solid #D7E2EC;
}
/*每一个li的高度是35px*/
#test li{
height:25px;
line-height:25px;
padding:5px;
border-bottom:1px dotted #D5D5D5;
margin-bottom:-1px;
}
JS部分
var dom=document.querySelector("li");
console.log(dom.offsetHeight);
//每一个li的高度是36px=(25+10+1)=36px
var ul=document.querySelector("ul");
//如果没有margin-bottom:-1px那么该值为36*5+2*2=184px!
//但是如果有了margin-bottom:-1px那么后面的元素会覆盖上面元素的border-bottom
//最后得到184-5=179px!
console.log(ul.offsetHeight);
那么我们有一个问题,后面的li元素虽然没有border-top,但是当上一个元素设置了margin-bottom:-1px时候,该元素的padding部分是否会覆盖掉上一个li元素的border-bottom?
解答:肯定会,但是如果后面的元素没有指定background-color那么是无法产生后面元素覆盖前面元素的效果的,除非有不同的背景色,而ul在这里的背景色是固定的,所以在视觉上就相当于没有重合一样的效果!
Test1
Test2
因为第一个div子元素设置了margin-bottom:-1px,所以后面的元素的div的border会和上面的border完全重合,但是如果margin-bottom:-10px时候你会发现这样一个情况,那就是后面的元素和上面的元素的border都存在!见 该图
用法3:margin对那些元素有效果?
Block水平:不要告诉我你懂margin一文中明确指定了,对于block水平的元素四个方向都是可以设定的,但是对于标准浏览器中,如果父元素的第一个子元素采用了margin来和父元素撑开距离这时候就会出现上面所说的垂直方向的margin重叠现象,除非父元素明确指定了padding-top/border-top,这时候我们强烈建议使用父元素的padding-top来撑开距离,保证没有margin重叠的情况下还有利于维护(相对于为第一个子元素设定paddingTop来说)!
inline水平:对于内联元素中(非置换元素)来说margin-top/margin-bottom失效,但是margin-left/margin-right还有效果
内联元素中还有一类特殊的元素-置换元素,这些个元素img|input|select|textarea|button|label|object虽然是内联元素,但margin依旧可以影响到他的上下左右!他们区别一般inline元素(相对而言,称non-replaced element)是:这些元素拥有内在尺寸(intrinsic dimensions),他们可以设置width/height属性。他们的性质同设置了display:inline-block的元素一致。
浮动元素:CSS布局奇淫巧计之-强大的负边距一文指出了负margin对浮动元素的影响:
通过给浮动元素设定margin负值也能导致元素之间存在覆盖
.float
{
float:left;
width:100px;
height:100px;
margin-right:-50px;
/*所有的浮动元素右边界收缩了50px,后续的元素会在收缩的距离上面显示*/
}
从这个例子我们知道,浮动元素也是受到负margin的影响的(浮动元素虽然脱离了文档流,但是对于inline水平的元素其仍然能够发现浮动元素的存在,所以说浮动不是完全意义上的脱离文档流)。有一点要注意:
inline/inline-block水平的元素浮动后都会变成block水平的元素
var dom=document.querySelector("#block");
console.log(document.defaultView.getComputedStyle(dom,null)['display']);//inline-block变成了block水平的元素
绝对定位元素:
负数margin对于绝对定位元素的影响主要有:利用绝对定位元素来实现元素垂直水平居中,不过这种居中的实现依然要知道元素本身的尺寸!同时,张鑫旭在“absolute绝对定位的非绝对定位用法”一文中用到了margin对绝对定位元素的影响得出:absolute定位与margin定位其实是没有什么冲突的,无论absolute元素时候设置了left/top值,其margin属性值都是可以起作用的!
很多经典的例子可以参考:负值之美:负margin在页面布局中的应用,我知道你不知道的负Margin等系列博客
总结:
(1)通过getBoundingClientRect可以获取到元素距离可视区域顶端的距离,因为body的margin默认是8像素,这是为什么要css reset!
(2)父子元素的margin-top的合并的问题的解决可以通过为元素设置border-top或者padding-top(但是建议父元素下第一个子元素用padding代替margin,该思想可以参考文章用padding还是用margin),然而兄弟元素的margin-top/margin-bottom的重叠就需要构建BFC了,因为默认如果两个元素属于同一个BFC就会发生margin-top/margin-bottom重叠现象!
(3)除了上面提到的"情况二"(父元素指定了宽度,而子元素通过margin-right/margin-lef负值t改变了元素自身的大小),margin-left/margin-right/margin-bottom/margin-top并不会改变元素本身的大小。margin-left为负数只会导致元素往左边移动,margin-top为负数是往上移动,而margin-bottom会改变元素的逻辑大小,使得后面的元素可能会覆盖元素自身(但是不会覆盖元素的border);margin-right也仅仅是修改元素的右边界,使得后续的元素可能覆盖元素自身!