理解並運用 CSS 的負 margin 值

Erichain發表於2016-10-17

本文樣式程式碼採用 SCSS。
瀏覽器相容性為 IE6+。

你的網頁中,不可能沒有使用過 margin。大多數情況下,我們採用的都是正數的 margin 值,可能有時候會用到負的 margin 值。在我們的印象中,負的 margin 值就類似於瀏覽器的 hack 一樣,不被人接受。但是,本文要說明的就是,負的 margin 值並不是 hack,這是正常範圍內的寫法。

Negative values for margin properties are allowed, but there may be implementation-specific limits. —— W3C

根據 W3C,margin 是能夠接受負值的,只是在具體實現上有一些區別。

那麼,設定 margin 為負值究竟會是什麼樣的效果呢?

與設定正值不同,margin 設定負值需要根據設定的方向以及元素是否浮動以及其定位方式來判斷最終的行為。

所以,具體行為按照以下幾種情況說明。

第一種情況:元素沒有設定浮動且沒有設定定位或者 positionstatic

如果元素沒有設定浮動並且沒有設定定位或者 position 屬性為 static 的情況下,對元素的 margin 設定負值會有以下的效果:

設定的 margin 的方向為 top 或者 left

當設定負值的 margin 的方向為 top 或者 left 的時候,元素會按照設定的方向移動相應的距離。

比如,設定 margin-left: -100px;。 那麼,元素會往左移動 100px。對於設定 margin-top 也是一樣的道理。

設定的 margin 的方向為 bottom 或者 right

當設定負值的 margin 的方向為 bottom 或者 right 的時候,元素本身並不會移動,元素後面的其他元素會往該元素的方向移動相應的距離,並且覆蓋在該元素上面。

比如,設定 margin-right: -100px;。那麼,元素本身並不會移動,後面的元素會向左移動 100px 到該元素上。對於設定 margin-bottom 也是同樣的道理。

同時,在元素不指定寬度的情況下,如果設定 margin-left 或者 margin-right 為負值的話,會在元素對應的方向上增加其寬度。效果就和設定 padding-left 或者 padding-right 一樣。

第二種情況:元素沒有設定浮動且 positionrelative

如果元素沒有設定浮動,但是設定了相對定位,設定 margin 為負值的時候,表現如下:

設定的 margin 的方向為 top 或者 left

當設定負值的 margin 的方向為 top 或者 left 的時候,元素也會按照設定的方向移動相應的距離。

設定的 margin 的方向為 bottom 或者 right

當設定 margin-bottom/left 的時候,元素本身也不會移動,元素後面的其他元素也會往該元素的方向移動相應的距離,但是,該元素會覆蓋在後面的元素上面 (當然,此處說的情況肯定是後面的元素沒有設定定位以及 z-index 的情況)。

第三種情況:元素沒有設定浮動且 positionabsolute

如果元素沒有設定浮動,但是設定了絕對定位,設定 margin 為負值的時候,表現如下:

設定的 margin 的方向為 top 或者 left

當設定負值的 margin 的方向為 top 或者 left 的時候,元素也會按照設定的方向移動相應的距離。

設定的 margin 的方向為 bottom 或者 right

由於設定絕對定位的元素已經脫離了標準文件流,所以,設定 margin-right/bottom 對後面的元素並沒有影響。

第四種情況:元素設定了浮動

肯定沒有既設定了浮動又設定絕對定位的情況,那樣太荒唐了。
設定了浮動的元素,再設定 postion: relative; 的話,元素的行為和單獨設定 float 是一樣的。

對於設定了浮動的元素,設定 margin 為負值的時候,表現如下:

如果設定的 margin 的方向與浮動的方向相同,那麼,元素會往對應的方向移動對應的距離。

比如:

.elem {
    float: right;
    margin-right: -100px;
}複製程式碼

該元素則會向右移動 100px。

如果設定 margin 的方向與浮動的方向相反,則元素本身不動,元素之前或者之後的元素會向鈣元素的方向移動相應的距離。

比如:

.elem {
    float: right;
    margin-left: -100px;
}複製程式碼

位於該元素左邊的元素則會向右移動 100px,同時覆蓋在該元素上。

如果後面的元素也設定了浮動的話,我們以一個具體的例子來說明。

<div class="container">
    <div class="left"></div>
    <div class="right"></div>
</div>複製程式碼
.container {
    min-height: 300px;
    margin: 30px auto;
    overflow: hidden;
    border: 1px solid #000000;

    .left {
        float: left;
        width: 400px;
        height: 200px;
        margin-right: -300px;
        background: purple;
    }

    .right {
        float: left;
        width: 300px;
        height: 200px;
        background: #cccccc;
    }
}複製程式碼

.left.right 都設定了浮動,在 .left 上設定了 margin-right: -300px;,那麼,.right 會向左移動 300px,從而覆蓋在 .left 上。這種行為與沒有既沒有設定浮動也沒有設定定位的表現類似。


到此,我們把設定負 margin 的各種情況以及在各種情況下的表現都大概瞭解了一遍。那麼,我們真正運用到實際中會是什麼樣子呢。

半遮擋的標題

原諒我措辭不好,大概就是下圖的效果:

理解並運用 CSS 的負 margin 值
|center

按照一般的思想,肯定是直接給 title 設定絕對定位,然後再將其調整過去。

但是,按照我們現在所說的,其實很簡單就能實現這個效果。

這裡只寫了主要部分的程式碼。

<div class="title">Hey This is title!</div>
<div class="content">Hah! This is content.</div>複製程式碼
.title {
    position: relative;
    width: 200px;
    height: 60px;
    margin-bottom: -30px;
    margin-left: -20px;
    background: #000000;
}

.content {
    max-width: 800px;
    height: 400px;
    padding: 0 50px;
    background: yellow;
}複製程式碼

我們為 title 設定了兩個 margin 的負值,分別是 margin-bottom: -30px;,以及 margin-left: -20px;

設定 margin-bottom 是為了讓 content 向上移動,設定 margin-left 是為了讓 title 向左移動一小段距離。

還有個需要注意的地方就是,需要給 title 設定 position: relative;,根據我們的第二種情況所說的,這樣,才能保證 title 覆蓋在 content 之上。

簡單的一列定寬的兩列流式佈局

根據我們的最後一種情況,通過設定 margin 為負值,我們可以很容易的實現一列定寬的兩列流式佈局。

<div class="container">
    <div class="left"></div>
    <div class="right"></div>
</div>複製程式碼
.left {
    float: left;
    width: 100%;
    height: 200px;
    margin-right: -300px;
    background: purple;
}
.right {
    float: left;
    width: 300px;
    height: 200px;
    background: #cccccc;
}複製程式碼

唯一需要注意的地方就是設定了 100% 寬度的元素上的 margin 負值的絕對值一定要和定寬的元素的寬度相同。

兩邊固定,中間自適應的三列布局

這是一個很老的話題了,以前也有各種實現的方式,比如雙飛翼佈局,或者聖盃佈局。

我們此處就以雙飛翼佈局來作示例。

先設定頁面結構:

<div class="container">
    <div class="center"></div>
    <div class="left"></div>
    <div class="right"></div>
</div>複製程式碼

此處我們沒有把 center 放在中間,具體原因後面會解釋。

然後,我們設定這三列都浮動:

.left,
.right,
.center {
    float: left;
    height: 500px;
}複製程式碼

同時為他們指定寬度:

.left {
    width: 300px;
    background: #000000;
}

.right {
    width: 400px;
    background: #00FFFF;
}

.center {
    width: 100%;
    background: #93c759;
}複製程式碼

現在我們要讓 left 在左邊,相當於就是讓它覆蓋在 center 的上面,所以,只需要這樣一句:

margin-left: -100%;複製程式碼

同時,要讓 right 在右邊,同理,這樣設定:

margin-left: -400px;複製程式碼

注意,此處的 margin 值的絕對值與 right 的寬度值相同。

其實,這樣設定,我們的三列布局就基本完成了。

那麼,我們為什麼要把 center 放在 left 和 right 之前呢?

這個其實涉及到元素的堆疊順序的知識 (這裡就不詳細講解了,後面有時間的話專門拿一篇文章來講解吧),此處簡單說明一下。

由於我們的三列都設定了浮動,所以,從某種意義上說,它們三個是在同一個平面的 (相當於 z-index 相同),那麼,這裡就不能根據 CSS 來判斷堆疊順序了。所以,此處的 HTML 結構就決定了它們的堆疊順序:所謂後來居上。

我們要讓 left 在 center 之上,所以,肯定需要讓 left 元素放在 center 之前。

所以,三列布局完整的 SCSS 程式碼如下:

.container {
    overflow: hidden;

    .left,
    .right,
    .center {
        float: left;
        height: 500px;
    }

    .left {
        width: 300px;
        margin-left: -100%;
        background: #000000;
    }

    .right {
        width: 400px;
        margin-left: -400px;
        background: #00FFFF;
    }

    .center {
        width: 100%;
        background: #93c759;
    }
}複製程式碼

References

margin-properties | W3C

The Definitive Guide to Using Negative Margins

雙飛翼佈局和聖盃佈局的對比

相關文章