Zerlinda's Blog

margin负值的应用场景详解

在了解margin之前,我们要先了解一下CSS的盒模型,点击这里查看

标准的盒模型由内而外依次是元素内容、内边矩、边框和外边距。内边距、边框和外边距可以用于一个元素的所有边,也可以单个边分别设置,完整的属性的写法应该包括四个值,依次是上右下左顺时针方向。个人觉得很重要的一点是:当设置某一元素的背景或者其定位(position)的具体参数(left,top等)时,其是以border为边界的。似乎这样一说,感觉padding在这里又有点content的意思了。言归正传,内边距和外边距最大的不同之处在于,外边距可以是负值,内边距却不行。存在即真理,对于margin的负值肯定是有可取之处,而且,适当的使用负值可以有意想不到的效果,下面具体来说:

与正值的 margin一样,margin 的负值同样可以被用于上下左右四个方向,把它所起到的效果总结为:

上方和左方的 margin 负值使该元素向上和左方向移动。

下方和右方的 margin 负值使该元素下方、右方的元素被拉向该元素。

还需要说明的一点的是,如果元素设置了border,不同的样式处理border的显示是不同的,具体看下面几个例子:

margin负值border的显示

上图的html和CSS代码比较简单,就不列出。外层一个ul列表设置了四个方向的边框,内层嵌套的li分别加上了上边框(第一个子元素没有做特殊处理,所以与父元素叠加一起是两倍)。这是不加特殊处理的效果。

margin负值border的显示

上图是对子元素li进行处理后的显示结果,设置margin-top为负值(具体值有border大小决定)。看起来第一条上边框是没有了,实际上细心可以发现,除了最后一个子元素,其他的每个子元素的显示高度(不包括border)是减少了的,具体的减少值便是margin的负值。所以这里要做一下说明了:在这个场景下,在对元素使用margin-top为负值之后,设置该属性的元素会向上偏移一定的距离,无论上一元素是否设置背景色,该元素都不会被上一个元素覆盖掉。实际看起来被覆盖掉的是上一元素。所以这里的border会正常显示。那么什么情况下当前元素会覆盖下一个元素呢?看下面:

tab8

我们仅仅需要对优先显示的元素加上两个属性,position:relative和background不透明属性(透明的话即使覆盖了下一元素还是会显示出来)。

了解了一些基本概念,下面来说一下一些常见的使用场景:

一、使用负值margin完成tab选项卡切换效果

效果图:

tab选项卡使用margin负值

通常看到效果图首先应当想到的是它dom的结构,外层的wrapper包含标题和内容,然后标题和内容分别是一部分,具体到每一部分再细分为list和div。确定下html结构之后就要思考对应的css是如何实现的了。对应的html代码如下:

<div class="wrapper">
  <ul class="topnav clearfix">
    <li class="list current">tab1</li>
    <li class="list">tab2</li>
    <li class="list">tab3</li>
    <li class="list">tab4</li>
  </ul>
  <div class="detail">
    <div class="list current">tab1</div>
    <div class="list">tab2</div>
    <div class="list">tab3</div>
    <div class="list">tab4</div>
  </div>
</div>

其实整个选项卡的重点就在于边框的显示问题上,根据盒模型原理中我们知道每一个元素都会有单独的边框和内外边距,如果父元素中嵌套了多个子元素,实际显示的效果则是它们的和(注意:margin存在折叠的情况,具体看这里)。

所以重点来了,如何处理这些边框呢?这时候,就轮到margin出场了,当然,是负值的margin。看具体的css代码:

html,body,ul,li {
    margin: 0;
    padding: 0;
}
ul,li {
    list-style: none;
}
.wrapper {
    margin: 50px auto;
    width: 400px;
}
.topnav {
    height: 28px;
    overflow: hidden;
    margin-bottom: -1px;
    border: 1px solid #6C92AD;
    border-bottom: none;
    background: #EAF0FD;
}
.topnav .list {
    float: left;
    margin-left: -1px;
    padding: 0 22px;
    line-height: 28px;
    border-left: 1px solid #6C92AD;
    border-right: 1px solid #6C92AD;
    font-weight: bold;
    color: #005590;
    text-align: center;
    cursor: pointer;
}
.topnav .list.current {
    position: relative;
    background: #fff;
}
.detail {
    border: 1px solid #6C92AD;
    padding: 15px;
}
.detail .list {
    display: none;
    text-align: center;
}
.detail .current {
    display: block;
}

实现原理:

最外层wrapper样式没有特殊处理。内层标题的ul列表设置了边框,但默认下方边框无,只有上左右边框。同样子元素li也只是设置了左右边框,之后为了解决边框加倍(左方边框+相邻元素有边框或父元素的边框),子元素li的margin-left值设置为-1px使自身向左偏移一单位,这样其实两边框重合在一起,显示的只有1px的宽度。还有一点需要设置暂且不提,继续往下看。

接下来是选项卡的内容显示了,这里对于内容区.detail的设置是四个方向的边框线全部显示,为此显示出来的效果是这样的:

tab使用margin负值

可以看出来对于标题和对应显示的内容区,中间应该是没有边框的。所以要去掉中间的边框,最终处理方式,整个ul列表的margin-bottom为-1px,对当前选中的标题li.current设置背景色为白色,最重要的一点是设定li的position属性为relative,不加这个属性的后果是虽然标题列表高度看起来少了1px,但底部的border却不会被隐藏掉。设置好所有的属性后,下方的元素向上偏移1单位,又因为当前的标题设置了背景色,所以下方的边框会被其覆盖掉,显示出来的就是最终的效果了。

其他类似的一些效果也可以通过margin负值来解决,这里举这一个简单的例子,就不再赘述。

二、两列自适应布局

自适应布局指宽度不固定,高度随内容变化的布局。这同样适用于三列中间自适应布局。

比如下面的布局:

margin负值使用场景

通常情况下这种两栏布局会让我们想到float或者是使用inline-block属性。其实,通过margin的设置也可以实现。

看一下html

<div class="wrapper">
  <div class = "left">这里是固定的内容。</div>
  <div class = "right">这里是自适应高度内容。这里是自适应高度内容。这里是自适应高度内容。这里是自适应高度内容。这里是自适应高度内容。这里是自适应高度内容。</div>  
</div>
<div class = "bottom">这里是wrapper的内容</div>

这里是css代码:

html,body,ul,li
{
    margin: 0;
    padding: 0;
}
ul,li
{
    list-style: none;
}
.wrapper
{
    width: 500px;
    height: 300px;
    background: #3E2929;
    border: 1px solid #000;
    overflow: hidden;
}
.left
{
    width: 200px;
    height: 300px;
    background: #E6FF5C;
}
.right
{
    margin-top: -300px;
    margin-left: 300px;
    background: #f9f9f9
}
.bottom
{
    width: 500px;
    height: 100px;
    background: #fafafa;
}

如果改变最外层wrapper的宽度会是如何呢,我们修改以下CSS属性:

.wrapper {

width: 480px;

}

效果:

margin负值应用场景

是不是左栏固定,右栏自适应了呢?至于三栏自适应,与此大同小异,感兴趣可以自己试一下,这里就不赘述了。

三、某些情况下使用margin负值代替绝对定位

严格说来,这种使用场景是跟第二种是类似的,仔细研究可以发现都是使用了margin负值使元素向左或者向上偏移一定的距离。而第二种方法能够有自适应的效果是因为上方和左方的margin设置了定值,而block元素默认占据整行,所以自适应的那一栏会自动根据容器的大小调整自己的宽度。使用margin负值代替绝对定位则是元素的宽高固定,使用margin来进行定位。看下面的例子:

margin负值使用场景

对应的html代码:

<div class = "wrapper">
    <div class = "content"></div>
	<div class = "logo"></div>
</div>

右上角的logo我们是不是可以通过设置margin来实现呢?也许你已经知道了,对margin-top设置content的负高度值,margin-left设置相应的值便可以轻松实现。是不是很方便?

四、去除首/尾的多余边框

这种场景无非就是在文章开头举的例子。去除列表首尾的多余边框,如果我们用margin负值就可以轻松解决了。缺点当然是内容显示的高度或者宽度会发生改变。当然,我们也不能忘记,同样可以通过伪元素选择器设置border为none来实现,但多一种思路,多一种选择嘛。

五、使用绝对定位加margin负值实现元素的垂直居中显示

还是先看例子,比如下面的效果,margin负值也是大显神通了。

margin负值使用场景

通过上面的一系列分析,相比对margin负值比较了解,这里也不赘述了,直接上代码:

html

<div class = "wrapper">
	<div class = "logo"></div>
</div>

css

.wrapper{
    position: relative;
    width: 100px; 
    height: 100px;
    margin: 50px ;
    background: #EAF0FD; 
    border: 1px solid #6C92AD; 
    overflow: hidden;
}
.logo {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 20px;
    height: 20px;
    margin-left: -10px;
    margin-top: -10px;
    background: #aaa;
}

评论(1)

发表评论

电子邮件地址不会被公开。 必填项已用*标注