css中的負邊距(negative margin)是布局中的一個常用技巧岭佳,只要運用得合理常常會有意想不到的效果适刀。很多特殊的css布局方法都依賴于負邊距斯嚎,所以掌握它的用法對于前端的同學來說捌锭,那是必須的募闲。
負邊距在普通文檔流中的作用和效果
那些沒有脫離文檔流的元素(指不是浮動元素也不是絕對定位步脓、固定定位的元素等),其在頁面中的位置是跟隨者文檔流的變化而變化的浩螺⊙セ迹看下面這幅圖:
負邊距對這些由文檔流控制的元素的作用是,會使它們在文檔流中的位置發(fā)生偏移要出,但這種偏移不同于相對定位鸳君,通過相對定位偏移后,其仍然會堅守著它原來占據(jù)的空間厨幻,不會讓文檔流的其它元素乘虛而入相嵌。而通過負邊距進行偏移的元素腿时,它會放棄偏移前占據(jù)的空間况脆,這樣它后面文檔流中的其它元素就會“流”過來填充這部分空間。還是通過例子來說明吧∨悖現(xiàn)在我們把上圖中的塊狀元素格了、行內(nèi)元素以及inline-block元素都設(shè)一個負邊距 margin:-10px; 看看會發(fā)生什么:
我們看到,黑灰色的塊狀元素好像向左和向上都分別嵌入了瀏覽器窗口的邊界里10px,然后塊狀元素下面的文字也爬到了它身上徽鼎,行內(nèi)元素向左移動蓋住了它前面的一個字盛末,它后面的文字也有一部分覆蓋在了它身上,inline-block的位置變化也很明顯否淤。
好了悄但,這點相信大家早就心知肚明了,就是負的邊距好像能減小元素在文檔流中的尺寸一樣石抡,但事實上檐嚣,它的尺寸大小并沒變,只是文檔流在計算元素位置的時候啰扛,會認為負邊距把元素的尺寸減小了嚎京,因為位置也就發(fā)生變化了嗡贺。這只是打個很形象的比喻,幫助大家理解一下鞍帝。還要注意的是诫睬,文檔流只能是后面的流向前面的,即文檔流只能向左或向上流動帕涌,不能向下或向右移動摄凡。
所以,一切只要是由文檔流決定的東西宵膨,負邊距就能起作用了架谎。
比如,一個沒有設(shè)定高度的塊狀元素辟躏,其高度是自動的谷扣,具體來說就是由它里面的文檔流最后的位置決定的粒没。假設(shè)它里面有一個出于文檔流中的子元素疲眷,高度為100px;那這時這個父元素的高度就等于子元素的高度100px了姜钳,如果子元素繼續(xù)增高瓤摧,那么父元素也會跟著增高蹬敲⊥笱玻可是如果這時子元素設(shè)一個負的margin-bottom,比如-20px列荔,因為負邊距會影響到文檔流玩郊,本來文檔流的高度是從父元素的最頂端到子元素的最底端這段高度籽御,現(xiàn)在子元素有一個margin-bottom:-20px;就相當于文檔流要向上退后20px,這樣整個文檔流的高度就減少了20px了练慕,那么父元素的高度也會跟著減少20px。在IE8+以及那些標準瀏覽器中技掏,這還需要父元素擁有一個overflow:hidden的屬性铃将,因為父元素的高度變了,但子元素的高度并沒有變哑梳,所以需要使子元素超出隱藏劲阎,但即使不設(shè)置overflow:hidden,父元素的高度也是變小了的鸠真,只不過這時子元素的高度會超出父元素悯仙。在IE6中則不需要,但需要觸發(fā)它的hasLayout屬性吠卷。所以以前所說的多列等高布局就是利用這個原理來實現(xiàn)的锡垄。
總之一句話,在文檔流中祭隔,元素的最終邊界是由margin決定的货岭,margin為負的時候就相當于元素的邊界向里收,文檔流認的只是這個邊界,不會管你實際的尺寸是多少茴她。
左和右的負邊距對元素寬度的影響
負邊距不僅能影響元素在文檔流中的位置寻拂,還能增加元素的寬度!
這個作用能實現(xiàn)的前提是:該元素沒有設(shè)定width屬性(當然width:auto是可以的)丈牢。
比如下圖的黑灰色部分是一個塊狀元素祭钉,它沒有設(shè)定寬度。它被包裹在一個寬度為400px,且水平居中的父元素中己沛。
現(xiàn)在給這個元素的設(shè)一個margin-right:-100px;
我們看到它的寬度的確變長100px;然后再給它設(shè)一個margin-left:-100px;
我們看到它變得更寬了慌核。
負的margin會改變元素的寬度,這的確很讓人費解申尼,如果說負邊距會改變元素在文檔流中的位置還是很好理解的話垮卓,那改變寬度這種現(xiàn)象還真的蠻讓人不可思議的。
那這貨有什么用途呢师幕?我就舉一個例子吧粟按。
想要創(chuàng)建上圖中黑框內(nèi)的幾個元素按順序排下來,中間帶些間隔的布局要怎么做霹粥?灭将,當然最簡單省事的方法就是利用浮動了。我們把黑框里面的子元素向左浮動后控,然后設(shè)一個合適的margin-right,是不是就辦到了呢庙曙?但因為外邊黑框的寬度是固定的,就是里面四個子元素的寬度加上三列間隔的寬度浩淘,所以靠近右邊邊界的子元素就不應(yīng)該有正向的margin-right了捌朴,否則這一行就只能容納三個子元素了。有人說那這還不簡單张抄,給靠近右邊界的那些子元素加一個class砂蔽,把它的margin-right設(shè)為0不就行了。這當然可以欣鳖,但如果這些子元素是在模板中通過循環(huán)動態(tài)輸出的察皇,那在循環(huán)的時候還得判斷哪些子元素是靠近右邊邊界的茴厉,如果是就加上一個class泽台。這樣做的話是不是就有點麻煩了?所以解決辦法是加大子元素的父容器的寬度矾缓,讓它能夠容納一行中有四個子元素加上四列間隔的寬度怀酷,然后最外面的的黑框的那個容器設(shè)一個overflow:hidden就行了。上面說了負的左右邊距能加大元素的寬度嗜闻,所以給子元素的父容器設(shè)一個合適的負的margin-right就可以了蜕依。當然你也可以直接在css中把子元素的父容器的寬度設(shè)寬一些,舉這個例子只是為了說明負邊距也是一種方法⊙撸看下完整的代碼:
負邊距對浮動元素的影響
負邊距對浮動元素的影響與負邊距對文檔流中元素的影響其實是差不多的友瘤。文檔流中元素的位置由文檔流的走向決定,浮動的元素也可以看成有一個“浮動流”存在檐束,不過浮動流既可以向左辫秧,也可以向右。
比如下圖是三個向左浮動的元素被丧,寬高都是100px:
現(xiàn)在把它們都設(shè)一個margin-right:-50px; 然后會變成這樣子:
我們看到后面的元素疊到了前面的元素上盟戏。
再看下面的圖:
我們把瀏覽器縮小了,然后因為寬度不夠甥桂,元素3掉下來了柿究。我們給元素3設(shè)一個margin-left:-80px;看看會怎么樣
這時我們看到元素3上去了,而且還覆蓋了元素2的一部分黄选。繼續(xù)元素3設(shè)為margin-left:-100px
這時元素3完全覆蓋住了元素2,當元素3設(shè)為:margin-left:-200px時:
我們看到元素3繼續(xù)向左移動并覆蓋住了元素1蝇摸。
現(xiàn)在想必大家都明白了負邊距對浮動元素位置的影響了吧。所以那些說得很好聽的什么圣杯布局办陷、雙飛翼布局啊什么的探入,都是利用這個原理實現(xiàn)的。就是某個元素雖然是寫在了后面懂诗,但可以通過負邊距讓它在瀏覽器顯示的時候是在前面的蜂嗽。這個以后可以再慢慢講。
負邊距對絕對定位元素的影響
絕對定位的元素定義的top殃恒、right植旧、bottom、left等值是元素自身的邊界到最近的已定位的祖先元素的距離离唐,這個元素自身的邊界指的就是margin定義的邊界病附,所以,如果margin為正的時候亥鬓,那它的邊界是向外擴的完沪,如果margin為負的時候,則它的邊界是向里收的嵌戈。利用這點覆积,就有了經(jīng)典的利用絕對定位來居中的方法:
看下效果:
但該方法的缺點是必須要知道要居中元素的高度和寬度。
實例:
稍微了解前端的人都知道熟呛,圣杯布局和雙飛翼布局是前端面試時必問的問題宽档,因為它既能體現(xiàn)你懂HTML結(jié)構(gòu)又能體現(xiàn)出你對DIV+CSS布局的掌握,畢竟我們學習CSS主要就是為了更好地布局帶來最好的用戶體驗嘛~
事實上庵朝,圣杯布局其實和雙飛翼布局是一回事吗冤。它們實現(xiàn)的都是三欄布局又厉,兩邊的盒子寬度固定,中間盒子自適應(yīng)椎瘟,也就是我們常說的固比固布局覆致。它們實現(xiàn)的效果是一樣的,差別在于其實現(xiàn)的思想肺蔚。
圣杯布局的出現(xiàn)是來自于a list part上的一篇文章In Search of the Holy Grail篷朵。比起雙飛翼布局,它的起源不是源于對頁面的形象表達婆排。在西方声旺,圣杯是表達“渴求之物”的意思。而雙飛翼布局則是源于淘寶的UED段只,可以說是靈感來自于頁面渲染腮猖。一起來看看淘寶的頭部實現(xiàn):
雙飛翼布局
通過縮放頁面就可以發(fā)現(xiàn),隨著頁面的寬度的變化赞枕,這三欄布局是中間盒子優(yōu)先渲染澈缺,兩邊的盒子框子固定不變,即使頁面寬度變小炕婶,也不影響我們的瀏覽姐赡。注意:當你縮放頁面的時候,寬度不能小于700PX柠掂,為了安全起見项滑,最好還是給body加一個最小寬度!
如果你有了那么一點理解以后,我們來看看圣杯布局的實現(xiàn):
第一步:給出HTML結(jié)構(gòu):
寫結(jié)構(gòu)的時候要注意涯贞,父元素的的三欄務(wù)必先寫中間盒子枪狂。因為中間盒子是要被優(yōu)先渲染嘛~并且設(shè)置其自適應(yīng),也就是width:100%宋渔。
第二步:給出每個盒子的樣式
header{width: 100%;height: 40px;background-color: darkseagreen;}
.container{ height:200px;overflow:hidden;}
.middle{width: 100%;height: 200px; background-color: deeppink;float:left;}
.left{? ? width: 200px;height: 200px;background-color: blue;float:left;}
.right{width: 200px;height: 200px;background-color: darkorchid;float:left;}
footer{width: 100%; height: 30px;background-color: darkslategray;}
第三步:看此時的效果圖
中間的三欄并沒有在一行內(nèi)顯示
大家可以看到州疾,三欄并沒有在父元素的一行顯示,就是因為中間盒子我們給了百分之百的寬度皇拣。所有左右兩個盒子才會被擠下來严蓖。
那么如何讓它們呈現(xiàn)出一行三列的效果呢?那就要讓左邊的盒子要到中間盒子的最左邊氧急,右邊的盒子到中間盒子的最右邊颗胡。換個想法,如果中間盒子不是100%的寬度态蒂,那么按照文檔流杭措,左邊的盒子一定會在中間盒子的后面顯示费什,接著顯示右邊的盒子钾恢。但是現(xiàn)在中間盒子是滿屏了的手素,所以左右兩個盒子被擠到下一行顯示。我們要做到的是讓左右兩個盒子都上去瘩蚪。此時泉懦,CSS的負邊距(negative margin)該上陣了。
第四步:利用負邊距布局
1.讓左邊的盒子上去
需要設(shè)置其左邊距為負的中間盒子的寬度疹瘦,也就是.left {margin-left:-100%;}崩哩。這樣左盒子才可以往最左邊移動。左邊產(chǎn)生負的margin言沐,所以向左移動100%的距離
2.讓右邊的盒子上去
需要設(shè)置其左邊距為負的自己的寬度邓嘹,也就是.right {margin-left:-200px;}。這樣右盒子才可以在一行的最右邊顯示出自己险胰。
第五步:看此時的效果圖
實現(xiàn)固比固布局
到這里汹押,是不是感覺很有成就感?但是很遺憾的告訴你起便,還沒結(jié)束哦棚贾!
我們現(xiàn)在的確是硬性的實現(xiàn)了固比固布局。但是要記住榆综,中間盒子是自適應(yīng)的寬度妙痹,所以中間盒子里的內(nèi)容會被左右盒子給壓住一部分。
比如現(xiàn)在我給中間盒子加很多的內(nèi)容鼻疮,大家看看效果圖:
中間盒子的內(nèi)容被蓋住
所以怯伊,我們的工作還沒停止。
第六步:讓中間自適應(yīng)的盒子安全顯示
首先:利用父級元素設(shè)置左右內(nèi)邊距的值判沟,把父級的三個子盒子往中間擠震贵。
代碼如下:.container{ padding: 0? 200px;}這里的200px是左右盒子的寬度。
效果如下:
利用父級的內(nèi)邊距將盒子往中間擠
我們可以看到水评,左右兩邊的內(nèi)邊距是有了猩系,但是中間盒子上的內(nèi)容還是被壓著。
其次:給左右兩個盒子加一個定位中燥,加了定位之后左右兩個盒子就可以設(shè)置left和right值寇甸。
代碼如下:
.left{ position: relative; left: -200px;}
.right{position: relative;right: -210px;
第七步:看最終效果圖
圣杯布局大功告成啦!
現(xiàn)在疗涉,圣杯布局終于搞定了拿霉,也實現(xiàn)了我們要的效果,左右側(cè)的盒子固定咱扣,中間盒子自適應(yīng)绽淘,而且中間盒子的內(nèi)容完全不受影響。你是不是也懂了呢?