负margin的一些应用及对其作用原理的理解

在前端CSS的布局中,负margin有很多可以应用到的地方,比如说等高布局、去除列表右边框等,不过在实际应用的时候都没有好好想过负margin为什么能做到这些,所以这两天好好地研究了一下负margin在这些布局应用中起到的作用。

首先我们先关注一下负margin对元素的影响:
在对普通文档流中的元素应用负margin的时候,margin-left和margin-right都能够使没有设置width(也就是width的值为auto)的块级元素的宽度变大。其实这个现象不难解释,我们知道,对于width为auto的元素来说,如果其左右margin为0的话,它的宽度会默认为100%,也就是和父元素内容的宽度相等。而如果设置了左右margin的话,将会满足margin-left + margin-right + width = 父元素内容区的宽度。说到这,我们就不难理解为什么应用左右负margin会使元素的宽度变大了,因为那部分margin的负值要加在元素的宽度上,使等式成立。在这种情况下我们就可能看到,子元素的内容区溢出了父元素的内容区,因为在文档流中子元素的左右margin边界是和父元素的左右padding边界相接的,应用了负margin的话,就相当于这个边界往里缩了,那么内容区自然也就“溢出”了。
不过上下margin的负值就不会使元素的高度变大了,因为在height这个属性上是没有width那样的父子元素之间满足的加法等式的存在的。对于一个元素来说,其高度是和他的子元素的高度有关的,而这个关系满足元素的高度 = 子元素的高度 + 子元素的上下margin。所以说如果给子元素应用了上下margin的话,被影响的不会是子元素的高度,而会是父元素的高度。在应用了负margin的情况下,父元素在获得子元素的高度值的时候,得到的将会是子元素本身的高度(border + padding + content)减去负margin的值,所以父元素计算出的高度就会变小。(不过前提是父元素有应用border或是通过before和after伪类创建了BFC来让子元素的垂直margin不会和父元素的垂直margin重叠,否则,这个负margin就会与父元素的margin重叠,导致影响到更上层的父元素的高度的效果)。用一个示意图来解释一下这个概念:
图1
如图,在应用了-50px的margin-top后,父元素container的高度变为了552,比子元素的高度600px小50(多出来了2是因为应用了1px的边界),我们可以看到子元素好像向上位移了50px,其实这也很好解释,是因为子元素的上下margin边界应该和父元素的上下padding边界贴合,所以应用了负的上margin,那么上margin边界就向内缩了。同理,如果再应用下边界负margin:
图2
可以看到高度又减小了50px,而且子元素的底部也溢出了50px,这和上边界内缩的原理是一样的。而这个边界内缩的原理其实就是绝对定位元素通过负margin实现居中的原理,我们后面再讲。
接下来根据上述负margin对元素的影响,我们就来讲解一下负margin在各种布局中的应用及我对其作用原理的理解(其实对元素的影响我没讲全,上面只讲了对块级元素的影响,而行内元素的影响没有讲,不过我想了想好像很少有需要对行内元素应用负margin达到的某种布局效果的应用,所以也就不深究了)

1.多列等高布局
在flex出现以前,实现这种布局一种常见的方法就是使用负margin的padding补偿法,也就是给需要实现等高的子元素按照业务需求设置一个足够大的padding-bottom,接着通过将margin-bottom设置为这个padding值的负值,然后对父元素设置overflow为hidden,就能实现多列的等高布局应用。当然,要对其中一个子元素应用float,让他们能够并排显示。具体的实现效果如下:
图3
这一效果的实现原理,就是应用我们上面讲到的垂直负margin对父元素高度的影响。在对子元素应用了一个很大的padding-bottom,并使用负的margin-bottom去抵消的情况下,该子元素的下边界就会缩到子元素的内容区的底部的位置,那么,此时对于父元素来说,这个子元素的高度就会被计算为它内容区的高度,而不会再多。接着,当我们给另一列应用了更大的高度值时,父元素的高度就会被计算为那个较高的子元素的高度。接着,由于背景也会被算在padding当中,所以在视觉上,我们就会觉得两列是等高的。
在这说句题外话,有了flex以后,我们就没有必要再这么实现等高布局了,在flex中,交叉轴上的布局模式align-items的默认值是stretch,也就是拉伸到同样的高度,所以现在我们用flex就能很轻松地实现等高布局了,而且不需要依赖应用background属性这种障眼法。

2.去除列表右边框
这个效果的意思和应用是说我们有时在呈现一个图片列表的时候,可能会想要其按照固定间距一行一行地排列下去,不过每行的最后一个元素是紧贴父容器边界的。效果大概是像这样:
图4
这个如果用js来实现的话,那我们一般就是用一个循环来给列表元素添加margin-right,并在循环中判断循环到每一行的最后一个的时候不赋margin。不过这样做有点麻烦,如果用CSS中的负margin来做就优雅且简单的多了,具体的作用原理是应用到了前文所述的负margin增加元素的宽度这一作用,先上代码:

<style>
    .container{ height:210px; width:460px; border:5px solid #000;}

    ul{
        height:210px;
        margin-right:-20px;
        list-style: none;
    }  /*一个负的margin-right,相当于把ul的宽度增加了20px*/

    li{
        height:100px;
        width:100px;
        background:#09F;
        float:left;
        margin-right:20px;
        margin-bottom:10px;
    }
</style>
<div class="container">
    <ul>
        <li>子元素1</li>
        <li>子元素2</li>
        <li>子元素3</li>
        <li>子元素4</li>
        <li>子元素5</li>
        <li>子元素6</li>
        <li>子元素7</li>
        <li>子元素8</li>
    </ul>
</div>

上述代码呈现的效果如下:
图5
可见,ul的宽度所示区域是溢出了container的边界的,也就是前文我们所述的负margin对元素宽度的影响作用。

3.绝对定位元素水平垂直居中
前面提到了负margin使边界内缩的原理其实就是绝对定位元素通过负margin实现居中的原理,这里用一个例子来讲解一下,还是先上代码:

.container {
    position: relative;
    height: 400px;
    border: 1px solid black;
    width: 600px;
    margin: 0 auto;
}

.ab {
    position: absolute;
    background-color: red;
    height: 100px;
    width: 100px;
    margin-top: -50px;
    margin-left: -50px;
    top: 50%;
    left: 50%;
}

<div class="container">
    <div class="ab"></div>
</div>

呈现的效果如下:
图6
这个效果实现的原理是,负margin使得子元素ab的上margin边界和左margin边界纷纷向内缩,我们将这个负margin值分别设置为该元素的高度和宽度的一半,使得这两个边界都内缩到了该元素的中心点处,然后我们将top和left设置为50%,由于这两个属性的百分比值是相对于父元素的高度和宽度计算的,所以我们就将子元素中心点到父元素边界的距离设置为了父元素长宽的一半,也就实现了绝对定位元素的水平垂直居中的效果。不过这个方法有一个局限性在于定位元素的必须是定高和定宽的,因为margin的百分比值也是相对于父元素的宽度和高度,所以不能通过将margin设置为-50%来应用这个方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值