重新认识margin和负margin的实际应用

margin是我们再熟悉不过的属性了,在限定模块之间的距离、制作流动性布局、解决IE6\7 bug以及margin负值时发挥了很大作用,所以有必要总结一下,特别是要对负margin的理解,因为它对日常工作的一些问题能很好的解决。

然后,在了解 负margin 之前,我们首先得非常清楚margin是啥玩样。

Margin 是什么

The margin clears an area around an element (outside the border). The margin does not have a background color, and is completely transparent.——w3schools.

因为 margin 是 outside the border’s element,所以我们一般称呼 margin 为 外边距 (padding is inside the border,所以也被称呼为 内边距),margin的最基本用途就是:控制元素周围空间的间隔。

margin的特性

  1. margin始终是透明的,所以设置 margin 来用来控制元素空间的间隔。
  2. 提供了缺省数值的缩写(这里就不介绍了),所以在实战中我一般都采用此种写法,一来方便修改,二来节省字符。
  3. 垂直外边据合并:当两个盒子垂直外边距相遇时,只有数值大的那个外边据的值才是起作用的(这跟padding有明显的区别,如果垂直方向设置了padding且相遇,则两个盒子之间的空白等于两者之和)。
  4. 一个盒子如果没有上补白(padding-top)和上边框(border-top),那么这个盒子的上边距会和其内部文档流中的第一个子元素的上边距重叠。
  5. margin 属性可以应用于几乎所有的元素,除了表格显示类型(不包括 table-caption, table and inline-table)的元素,而且垂直外边距对非置换内联元素不起作用。

以上举的第四个特性,看如下事例代码:

css代码

.example{background:#efc}
.margin-merge1{margin:20px 0 0;}
.margin-merge2{border-top:1px solid #000}
.margin-merge3{margin:40px;background-color:red}

html代码

<div class=”example”>..</div>
<div class=”example”>
<div class=”example margin-merge1″>..</div>
</div>

margin合并后的效果

不设置宽高是为了让所有浏览器的实验条件相同,因为设置宽高处罚了IE的layout,IE中这个bug也就消失了
因为没有上补白(padding-top)和上边框(border-top),那么这个盒子的上边距会和其内部文档流中的第一个子元素的上边距重叠。

想要的效果

不设置宽高是为了让所有浏览器的实验条件相同,因为设置宽高处罚了IE的layout,IE中这个bug也就消失了
因为没有上补白(padding-top)和上边框(border-top),那么这个盒子的上边距会和其内部文档流中的第一个子元素的上边距重叠。
垂直方向上margin 合并

对于垂直外边距合并的解决方案就是,为父元素例子中的middle元素增加一个border-top或者padding-top即可解决这个问题。

产生以上情况的根本原因,也可以换句话这么说:子元素的父元素的的如果没设置border-top或者padding-top,父元素的第一个子元素就会不断一层一层的找自己祖先元素。所以只要给父元素设置border-top或者padding-top,就能解决这个问题。由此我们下次应该注意什么时候用 margin 什么时候用padding。不能因为这个bug,而给父元素增加没必要的border-top或者padding-top。解决这个问题的另外一个方法就是:分别设置_zoom:1;overflow:hidden分别触发 ie的layout创建CSS 2.1的Block Formatting Contexts来避免margin的重叠。

而究其根本原因,是因为:在margin属性中一共有两类参考线,top和left的参考线属于一类,right和bottom的参考线属于另一类。top和left是以外元素为参考,而 right和bottom是以元素本身为参考。所谓外元素就是本元素的边界元素(再白话点的解释就是元素的紧邻元素,父元素或者兄弟元素),而元素本身呢,确切含义是指以自身为参考来影响周围元素的位置(实质即为影响下边和右边相邻元素的参考线)。所以才会出现以上事例中 父元素中的第一个子元素越级将自己的margin-top当成父元素的margin-top执行的情况。

然后,如果是负值则是位移方向相反。所以得出以下结论:

当margin四个值都为正数值的话,那么margin按照正常逻辑同周围元素产生边距。当元素margin的top和left是负值时会引起元素的向上和向左位置移动。而当元素margin的bottom和right是负值时会影响右边和下边相邻元素的参考线。

margin 和 padding 的区别

  1. margin会重叠,而padding也不会重叠,而是相加。
  2. margin可以有正值和负值,而padding则只允许正值。
  3. margin是outside the border;而padding则是inside the border

常见的浏览器下margin出现的bug

margin作为布局中重要的属性,而浏览器下因margin出现的各种bug,记录如下,希望大家补充

  1. IE6中双边距Bug

    发生场合:当给父元素内第一个浮动元素设置margin-left(元素float:left)或margin-right(元素float:right)时margin加倍。

    解决方法:给浮动元素加上_display:inline或者用padding-left margin-left

    原理分析:块级对象默认的display属性值是block,当设置了浮动的同时,还设置了它的外边距就会出现这种情况。也许你会问:“为什么之后的对象和第一个对象之间就不存在双倍边距的Bug”?因为浮动都有其相对应的对象,只有相对于其父对象的浮动对象才会出现这样的问题。第一个对象是相对父对象的,而之后对象是相对第一个对象的,所以之后对象在设置后不会出现问题。为什么display:inline可以解决这个双边距bug,首先是inline元素或inline-block元素是不存在双边距问题的。然后,float:left等浮动属性可以让inline元素haslayout,会让inline元素表现得跟inline-block元素的特性一样,支持高宽,垂直margin和padding等,所以div class的所有样式可以用在这个display inline的元素上。

  2. IE6/7下ul/ol标记消失bug

    发生场合:当ul/ol触发了haslayout并且是在ul/ol上写margin-left,前面默认的ul/ol标记会消失。

    解决方法:给li设置margin-left,而不是给ul/ol设置margin-left、去掉触发haslayout的属性也可以设置list-style:inside;

    原理分析:IE6/7浏览器Bug

  3. IE6中浮动元素3px间隔Bug

    发生场合:发生在一个元素浮动,然后一个不浮动的元素自然上浮与之靠近会出现的3px的bug。

    解决方法:右边元素也一起浮动;或者为右边元素添加IE6 Hack _margin-left:-3px;从而消除3px间距。

    原理分析:IE6浏览器缺陷Bug。

  4. IE6/7负margin隐藏Bug

    发生场合:当给一个有hasLayout的父元素内的非hasLayout元素设置负margin时,超出父元素部分不可见。

    解决方法:去掉父元素的hasLayout;或者赋hasLayout给子元素,譬如position:relative

    原理分析:IE6/7独有的hasLayout产生问题。

  5. IE6/7下margin与absolute元素重叠bug

    发生场合:双栏自适应布局中,左侧元素absolute绝对定位,右侧的margin撑开距离定位。在IE6/7下左侧应用了absolute属性的块级元素与右边的自适应的文字内容重叠。

    解决方法:把左侧块级元素更改为内联元素,比如把 display:inline

    原理分析:这是由于IE6/IE7浏览器将inline水平标签元素和block水平的标签元素没有加以区分一视同仁渲染了。属于IE6/7浏览器渲染Bug。

  6. IE6/7/8下auto margin居中bug

    发生场合:给block元素设置margin auto无法居中

    解决方法:出现这种bug的原因通常是没有Doctype,然后触发了ie的quirks mode,加上Doctype声明就可以了。

    原理分析:缺少Doctype声明。

  7. IE8下input[button | submit] 设置margin:auto无法居中

    发生场合:ie8下,如果给像button这样的标签(如button input[type=”button”] input[type=”submit”])设置{ display: block; margin:0 auto; }如果不设置宽度的话无法居中。

    解决方法:可以给为input加上宽度。

    原理分析:IE8浏览器Bug。

负Margin的应用

了解完margin,把平时能将其应用到实战中例举出来。

对显示border的应用

在选项卡边框线的处理

html代码

<div id="demoTab" class="demoTab">
    <ul class="demoTabNav clearfix">
        <li class="demoTabList current">前端</li>
        <li class="demoTabList">实战</li>
        <li class="demoTabList">交互</li>
        <li class="demoTabList">优化</li>
    </ul>
    <ul class="demoTabBd">
        <li class="demoTabContent current">这是第一个选项卡的内容。</li>
        <li class="demoTabContent">这是第二个选项卡的内容。</li>
        <li class="demoTabContent">这是第三个选项卡的内容。</li>
        <li class="demoTabContent">这是第四个选项卡的内容。</li>
    </ul>
</div>

css代码

.demoTab{width:400px; font:14px/1.5 Microsoft YaHei,verdana,Helvetica,Arial,sans-serif;}
.demoTab .demoTabNav{height:28px; overflow:hidden; *zoom:1;margin-bottom:-1px; border:1px solid red; border-bottom:none; background:#EAF0FD;}
.demoTab .demoTabList{float:left; margin-left:-1px;  padding:0 22px; line-height:28px; border-left:1px solid red; border-right:1px solid red;  font-weight:bold; color:#005590; text-align:center; cursor:pointer;}
.demoTab .demoTabList.current{position:relative; background:#fff;}
.demoTab .demoTabBd{border:1px solid red;padding:15px;}
.demoTab .demoTabContent{display:none;}
.demoTab .demoTabContent.current{display:block;}

DEMO

负margin在选项卡边框线的应用

如上DEMO,使用margin-bottom:-1px;解决选项卡下边框显示的问题,使选项卡主体部分往上提了 -1px(或者也可以在主体部分使用margin-top:-1px;具体情况具体分析),而选项卡上的选项左右边框,可以让每个div都有左右1像素的边框,然后margin-right:-1px;或是margin-left:-1px;让之间的边框重叠来达到效果,像这种隐藏首末边框的不仅在水平方向上而且在竖直方向上也有应用,代码如下:

html代码

<ul class="cross">
    <li>这里是一条信息</li>
    <li>这里是一条信息</li>
    <li>这里是一条信息</li>
    <li>这里是一条信息</li>
    <li>这里是一条信息</li>
    <li>这里是一条信息</li>
</ul>

<ul class="vertical">
<li>这里是一条信息</li>
<li>这里是一条信息</li>
<li>这里是一条信息</li>
<li>这里是一条信息</li>
<li>这里是一条信息</li>
<li>这里是一条信息</li>
</ul>

css代码

.cross{overflow: hidden;zoom: 1;}
.cross li{float: left;padding: 0 11px 0 10px;border-left:1px solid #AAA;margin-left:-1px;}
.vertical{overflow: hidden;_zoom:1; }
.vertical li{border-top:1px dashed #CEE1EE;padding:5px 0;margin:-1px 0 0}

DEMO

  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
  • 这里是一条信息
隐藏首(末)边框

在流动性布局中的应用

在介绍 Block Formatting Context可以阻止元素覆盖浮动盒模型 中出现过一个流动布局,它是 利用 创建CSS2.0中的Block Formatting Context和IE触发layout 来实现的,而使用 负margin 实现的流体布局好处之一就是让 main部分显示在html结构最前面。

网很多介绍文章,我就直接贴代码了

css代码

.grid{width:960px;margin:0 auto;overflow:hidden;zoom:1;}
.main-wrap{width:100%}
.main{background-color:#FAFAFA}
.sub{width:300px;background-color:#FF3377}
.extra{width:100px;background-color:#fd5}
.main-wrap,.sub, .extra{float:left;}
.main,.sub,.extra{height:200px;}
.grid1 .main{margin:0 400px 0 0}
.grid1 .sub{margin:0 0 0 -400px;}
.grid1 .extra{margin:0 0 0 -100px}
.grid2 .main{margin:0 300px 0 100px}
.grid2 .sub{margin:0 0 0 -300px}
.grid2 .extra{margin:0 0 0 -960px}
.grid3 .main{margin:0 0 0 300px}
.grid3 .sub{margin:0 0 0 -960px;}
.grid4 .main{margin:0 0 0 -960px;}
.grid4 .extra{margin:0 0 0 -100px;}

html代码

<div class="grid grid1">
    <div class="main-wrap">
        <div class="main">main</div>
    </div>
    <div class="sub"> sub</div>
    <div class="extra"> extra</div>
</div>

DEMO

main
sub
extra
main
sub
extra
main
sub
main
extra
流动布局中负margin的应用

css代码

.grail{overflow:hidden;zoom:1}
.grail-main{float:left;width:100%;background-color:#FAFAFA}
.grail-sub{float:left;width:300px;margin:0 0 0 -300px;background-color:#FF3377}
.grail-extra{float:left;width:100px;margin:0 0 0 -100%;background-color:#fd5}

html代码

<div class="grail">
    <div class="grail-main">main</div>
    <div class="grail-sub"> sub</div>
    <div class="grail-extra"> extra</div>
</div>

DEMO

main
sub
extra
圣杯布局

其他应用

margin的其他应用:

  • 使用 使用margin负边距剪切图片 控制box的大小,进而控制其中img的显示区域。
  • 使用 负margin 来代替某块用 relative/absolute 定位。
  • 现在想不起来了,想到了就来更新

本文源链接:http://www.html5jscss.com/negative-margin.html

转载请注明《重新认识margin和负margin的实际应用》| html5jscss