最近看到一篇讲解CSS 负边距的文章: http://segmentfault.com/a/1190000003750411?utm_source=Weibo&utm_medium=shareLink&utm_campaign=socialShare 看后感觉如沐春风。以下是我的一点理解:
首先是原文中的结论:
- margin为正时,top、left属性是以content上(左)或垂直上方相连元素margin的下(右)边为参考线垂直向下(右)位移。
- margin为负时,right、bottom属性是元素本身的border右(下)边为参考线水平向右(下)位移。
- 盒子最后的显示大小等于盒子的
border
+padding
+正margin
,而负margin
不会影响其大小。- margin为负且盒子static时:
- 若属性为top、left,盒子将被拉进指定的方向;
- 若属性为bottom、right,将后续的元素拖拉进来,覆盖本来的元素。
- 若width没有被设置,设定负margin-left/right会将元素拖向对应的方向,并增加宽度,此时的margin的作用就像padding一样
我们按照这个结论,对原文中提的两个demo,进行试验并进行详解。
选项卡demo
按照原文中的结论,如果对上面的块级元素的下边距设置为负值,那么下面的块级元素就会被拉上来,而且会覆盖上面的标签。等等,我们要的效果是你下面的内容区有边框,但是上面的菜单栏中的选中项会覆盖掉下面边框的一部分吗?
我一开始在这里也有一些疑惑,因为效果是上压下,但原文结论是下压上。这不就矛盾了吗。但是当我按照原文写出demo后,发现了玄机。首先说明结论是正确的。
一下是我的demo代码:
html:
1 <div class="tap-wrap"> 2 <ul class="tap-group"> 3 <li class="tap-item"><a class="btn-tap" href="javascript: void(0);">前端</a></li> 4 <li class="tap-item"><a class="btn-tap" href="javascript: void(0);">实战</a></li> 5 <li class="tap-item"><a class="btn-tap" href="javascript: void(0);">交互</a></li> 6 <li class="tap-item"><a class="btn-tap" href="javascript: void(0);">优化</a></li> 7 </ul> 8 <ul class="content-group"> 9 <li class="content-item active"><p class="content-txt">这是第一个选项卡的内容。</p></li> 10 <li class="content-item"><p class="content-txt">这是第二个选项卡的内容。</p></li> 11 <li class="content-item"><p class="content-txt">这是第三个选项卡的内容。</p></li> 12 <li class="content-item"><p class="content-txt">这是第四个选项卡的内容。</p></li> 13 </ul> 14 </div>
css:这里我为了说明问题,加入了一些辅助的CSS
1 a{ text-decoration: none; } 4 5 .main-wrap{ 6 margin: 50px auto 0 auto; 7 width: 700px; 8 height: 500px; 9 border: 1px solid #000; 10 } 11 16 .tap-group{ 17 list-style: none; 18 overflow: hidden; 19 margin-bottom: -1px; 20 background-color: #999; /********************************************辅助颜色*/ 21 } 22 23 .tap-item{ 24 float: left; 25 width: 100px; 26 height: 50px; 27 line-height: 50px; 28 text-align: center; 29 border-top: 1px solid red; 30 border-right: 1px solid red; 31 } 32 33 .tap-item.active{ background-color: #fff;} 36 .tap-item:first-child{ border-left: 1px solid red; } 40 41 .content-group{ 42 width: 403px; 43 height: 200px; 44 border: 1px solid red; 45 overflow: hidden; 46 background-color: #eee; 47 } 48 .content-item{width: 100%; height: 100%; display: none;} 54 .content-item.active{ display: block; }
js:
1 $(document).ready(function(){ 2 $(‘.tap-item‘).on(‘click‘, function(){ 3 var index = $(this).index(); 4 $(this).add(‘.content-item:eq(‘+index+‘)‘).addClass(‘active‘).siblings().removeClass(‘active‘); 5 }); 6 });
我们打开调试器,将菜单栏(.tap-group)原来的 margin-bottom: -1px; 改成 -18px 这样你就会看到这样的效果:
也就是说原文中覆盖内容区边框的其实不是菜单栏,而是tab选项自身(.tap-item),那么为什么下面的内容区覆盖上面的菜单栏,但是菜单栏中的tab选项却覆盖了下面的内容去呢(tab选项 覆盖--> 内容区 覆盖--> 菜单栏)?
下面来进行几种猜想:
猜想1: z-index
这和菜单栏的tab选项布局有关,因为我们采用的是浮动,浮动后<li>标签脱离了文档流,z-index高于正常的元素。
我们验证一下,将浮动改成display: inline-block;这样每一个tab选项都是未脱离文档流的,但是此例中,仍能实现这个效果。
因此这种猜想不成立。
猜想2: BFC
既然float:left;和display: inline-block; 都可以完成这种效果(tab选项 覆盖--> 内容区 覆盖--> 菜单栏)。那么我们是不是可以抽出这个问题把思路放在单纯的负边距覆盖问题上;看看是不是每种BFC触发条件都能做到。
我们不受到横向布局的限制,因此我打算再下一个demo,专门来测试BFC对负边距布居中覆盖关系的影响。
html:
1 <div class="main-wrap"> 2 <div class="box1"> 3 <p class="txt">负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距负边距</p> 4 </div> 5 <div class="box2"></div> 6 </div>
不加入BFC,仅仅加入负边距。
css:
1 .main-wrap{ 2 margin: 50px auto 0 auto; 3 width: 700px; 4 height: 500px; 5 border: 1px solid #000; 6 } 7 8 .box1{ 9 background-color: #453453; 10 width: 100px; 11 height: 100px; 12 border: 1px solid red; 13 margin: 0px 0px -34px 0px; 14 } 15 16 .box1 .txt{ 17 background-color: #fff; 18 height: 100px; 19 } 20 21 .box2{ 22 background-color: #453453; 24 height: 100px; 25 border: 1px solid red; 26 margin: 0px -30px 0px -40px; 27 }
我们来看一下效果:
我们打开调试器: 分别测试加入BFC的触发条件,看看是不是会出现预期的效果(.box .txt)的背景色覆盖下面的块级元素。
发现可以达到预期效果的有: position: absolued 或 relative; float: left; display:inline-block -webkit-inline-box inline-flex 背景大小为我们设定的100px ,见效果图 3。 (虽然不是bfc,但也一起看一下 inline inline-table 白色背景包裹文本内容大小, 见效果图 4。)
效果图3: 效果图4:
但是BFC条件中: 我设置overflow: hidden; display : table-cell, table-caption, flex;均没有效果。
因此这个效果和BFC没有直接的关系,同时我们也进一步发现了一些条件:
position: absolued 或 relative; float: left; display:inline-block -webkit-inline-box inline-flex
因此得出: 如果设置margin-bottom后下方元素会被拉上来,并覆盖自身,但是,如果对自身内部元素(有背景色和高度)进行一下设置position: absolued 或 relative; float: left; display:inline-block -webkit-inline-box inline-flex中的一项,内部元素会覆盖下方被吸上来的元素。
负margin实现两列等高布局
对于这个demo原文中没有做出解释,简单运行了一下,发现这里的等高,其实是利用background-color包含padding的特点利用一个较大的padding填充补齐空间再用负的margin拉回文档流中的占位,最后父级元素overflow: hidden;进行截断。