margin 爲負值產生的影響和常見佈局應用
原文鏈接: margin爲負值產生的影響和常見佈局應用
前言
前幾天去了一家公司面試前端,問了我雙飛翼的佈局,說實話,之前真沒好好研究過實現原理。
面試回來,查了下,主要都是用到了 margin-left 負數產生的效果。
所以今天整理些 margin:負數會對哪些元素或者定義產生影響、margin 爲負值在 web 佈局中的應用做下總結。(不能說最全,我已經盡力收集整理)
一、margin 爲負值產生的影響
對於自身的影響
當元素不存在 width 屬性或者(width: auto) 的時候,負 margin 會增加元素的寬度,看下下面的例子
<div class="contaienr1">
<div class="box1">
I dont have the width
</div>
</div>
.contaienr1 {
width: 400px;
height: 400px;
border: 1px solid red;
margin: 0 auto;
}
.box1 {
border: 1px solid green;
margin-left: -20px;
margin-right: -20px;
}
可以看到 box1 增加了 20px 寬度,margin-left 和 margin-right 都是可以增加寬度
注意:
margin-top 爲負值不會增加高度,相應的會減少自身的高度,進而產生向上位移
margin-bottom 爲負值不會產生位移,會減少自身的供 css 讀取的高度。
<div class="wrapper">
<div class="box">
box
</div>
</div>
<div class="wrapper">
<div class="box2">
box
</div>
</div>
.wrapper {
margin-top: 100px;
border: 1px solid red;
}
.box {
width: 50%;
margin-bottom: -25px;
background-color: rgba(90, 243, 155, 0.8);
height: 50px;
}
.box2 {
width: 50%;
margin-top: -25px;
background-color: rgba(90, 243, 155, 0.8);
height: 50px;
}
var x = $('.box').height()
console.log(x);
在 jsrun 中打開示例
下文的應用段落就會利用這個特點做一個多列等高佈局。
對文檔流的影響
元素如果用了 margin-left: -20px;毋庸置疑的自身會左偏移 20px 和定位(position: relative) 有點不一樣的是,在其後面的元素會補位,也就是後面的行內元素會緊貼在此元素的之後。總結,不脫離文檔流不使用 float 的話,負 margin 元素是不會破壞頁面的文檔流。
<div>
<h2>文檔流-行內元素</h2>
<div>
我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章<span class="inline-margin">我是一大堆文章</span>我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章
</div>
</div>
<div>
<h2>文檔流-塊元素</h2>
<div>
我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章我是一大堆文章
<div class="box">
我是個盒子
</div>
我是後面的元素我是後面的元素我是後面的元素我是後面的元素我是後面的元素我是後面的元素我是後面的元素我是後面的元素我是後面的元素我是後面的元素
</div>
</div>
.inline-margin {
color: #eee;
margin-left: -50px;
background: rgba(0, 0, 0, 0.1)
}
.box {
height: 50px;
width: 50px;
background: blue;
margin-top: -25px;
margin-left: -25px;
}
所以如果你使用負 margin 上移一個元素,所有跟隨的元素都會被上移。
對浮動元素的影響
先定義 3 個 box
<div class="container1">
<div class="flb flbox1">box1</div>
<div class="flb flbox2">box2</div>
<div class="flb flbox3">box3</div>
</div>
.flb {
float: left;
width: 100px;
height: 100px;
}
.flbox1{background-color: rgba(33, 114, 214, 0.8);}
.flbox2{background-color: rgba(255, 82, 0, 0.8);}
.flbox3{background-color: rgba(90, 243, 151, 0.8);}
demo1, 給他們都加上 margin-left: -25px
.flb{
float: left;
width: 100px;
height: 100px;
margin-left: -25px;
}
.flbox1{background-color: rgba(33, 114, 214, 0.8);}
.flbox2{background-color: rgba(255, 82, 0, 0.8);}
.flbox3{background-color: rgba(90, 243, 151, 0.8);}
可以看出 3 個盒子都向左移動 25px;
box1自身向左移動了25px,box2又覆蓋了其25px,所以我們就看到了“寬度”爲50px的box1
box2,box3以此類推!
讓我再看看 margin-left: -50px;的情況
如果只給 box3 設置 margin-left: -200px
.flb{
float: left;
width: 100px;
height: 100px;
}
.flbox1{background-color: rgba(33, 114, 214, 0.8);}
.flbox2{background-color: rgba(255, 82, 0, 0.8);}
.flbox3{background-color: rgba(90, 243, 151, 0.8);
margin-left: -200px;}
看一看到 box3,向左偏移了 200px,完全將 box1 覆蓋了,box3 下面還能隱約的看到了 box1.
在 jsrun 中打開上述示例
總結
負 margin 會改變浮動元素的顯示位置,即使我的元素寫在 DOM 的後面,我也能讓它顯示在最前面。聖盃佈局、雙飛翼佈局啊什麼的,都是利用這個原理實現的。
對絕對定位影響
<div class="absolute"></div>
.absolute{
position: absolute;
top:50%;
left:50%;
height: 200px;
width: 200px;
background-color: #ccc;
margin-top: -100px;
margin-left: -100px;
}
對於絕對定位元素,負 margin 會基於其絕對定位座標再偏移,
唯一的缺點就是你必須知道這個絕對定位元素寬度和高度才能設置負 margin 值使其居中瀏覽器窗口,
若對於不確定寬度和高度可以用
transform: translate3d(-50%,-50%,0);
使用translate3d可以開啓GPU加速,就不用cpu去從新計算所有點素位置,開啓GPU加速後,GPU自動將這個元素放在一個新的“層”,直接偏移這個“層”來提高渲染速度(個人理解,若有錯誤歡迎指正)。
二、margin 爲負值的常見佈局應用
左右固定,中間自適應(雙飛翼)
雙飛翼的好處:
- 可以讓主要內容出現在 dom 結構的前面,先將主要內容渲染
- 中間自適應,兩邊固定寬度的效果
<div class="main">
<div class="main-content">
main content
</div>
</div>
<div class="left">left</div>
<div class="right">right</div>
* {
margin: 0;
padding: 0
}
.main {
float: left;
width: 100%;
}
.main .main-content {
margin: 0 210px;
background-color: rgba(33, 114, 214, 0.8);
height: 500px;
}
.left {
width: 200px;
float: left;
background-color: rgba(255, 82, 0, 0.8);
margin-left: -100%;
height: 200px;
}
.right {
width: 200px;
height: 200px;
background-color: rgba(90, 243, 151, 0.8);
float: left;
margin-left: -200px;
}
去除列表右邊框
利用負 margin 增加寬度的特點,舉個在實例中應用例子
對於圖片列表,我會常常設計成兩邊對齊,中間元素平均分佈,類似下面的佈局。
想讓每一排最後一個對齊父元素的右邊框,一般都兩種處理,第一種會在給個循環體內的最後一個添加一個float:right屬性或者做特殊處理,但這樣還需要後端配合,我們自己能解決的就儘量自己解決。還有就是用css3的flexbox佈局能解決這個兩邊對齊,但是這種佈局兼容性不好,你的用戶用IE的話,不推薦!
so,負margin就能發揮其增加元素寬度的特點,完美的解決這個問題!
<div class="container">
<ul class="list">
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
<li>我是一個列表</li>
</ul>
</div>
* {
padding: 0;
margin: 0;
}
ul {
list-style: none;
}
.container{
margin:0 auto;
width: 500px;
border: 1px #ccc solid;
margin-bottom: 20px;
overflow: hidden;
}
.list{
overflow: hidden;
margin-right: -10px;
}
.list li{
width:92px;
height: 92px;
background-color: #ccc;
margin-bottom: 20px;
float: left;
margin-right: 10px;
}
計算公式:
假設一橫列展示的數量爲x,元素見的間距爲y,父距寬度z
則元素的寬度=(z-y(x-1))/x
本例中li的寬度爲(500-10(5-1))/5=92
負邊距 + 定位 : 水平垂直居中
上面已經舉例,用了負margin會向對應方向偏移的特點!
去除列表最後一個 li 元素的 border-bottom
很多情況下,我們會用 li 標籤來模擬表格,再給 li 標籤添加下邊距的時候,最後一個 li 標籤表格會和父元素的邊框靠在一起,會顯得整個 table 的底部邊框會比其他的邊粗一些!
<ul class="table">
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
</ul>
<ul class="table t2">
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
<li>I am li</li>
</ul>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
margin-top: 20px;
width: 200px;
margin-left: auto;
margin-right: auto;
}
ul.table {
border: 1px #ccc solid;
}
ul.table li {
border-bottom: 1px #ccc solid;
}
.t2 {
margin-top: 20px;
margin-bottom: 20px;
}
ul.table.t2 li:last-child {
margin-bottom: -1px;
}
多列等高
利用 margin-bottom 爲負值會減少 css 讀取元素高度的特性,加上 padding-bottom 和 overflow: hidden,就能做一個未知高度的多列等高佈局(當然也可以用 table)
<div class="container">
<div class="left">
height: 50px
</div>
<div class="main">
height: 100px;
</div>
<div class="right">
height: 30px;
</div>
</div>
.container {
margin: 0 auto;
width: 100%;
overflow: hidden;
}
.left {
height: 50px;
width: 33.33%;
margin-bottom: -5000px;
padding-bottom: 5000px;
float: left;
background-color: rgba(33, 114, 214, 0.8);
}
.main {
height: 100px;
margin-bottom: -5000px;
width: 33.33%;
float: left;
padding-bottom: 5000px;
background-color: rgba(255, 82, 0, 0.8);
}
.right {
width: 33.33%;
float: left;
margin-bottom: -5000px;
padding-bottom: 5000px;
background-color: rgba(90, 243, 151, 0.8);
}
雖然設置了 5000 的內邊距,但是我用-5000的外邊距去抵消掉,所以對於父元素來說(上文所說的 css 能讀取的高度),他還是原來的高度(但其自身實際高度爲 5000px + 本來高度),然後父元素在加上 overflow: hidden;以最高的高度進行裁切,所以就有了看起來"等高"的 3個 div。