如何實現一個三欄自適應佈局,左右各100px,中間隨着瀏覽器寬度自適應?css
第一個想到的是使用table佈局,設置table的寬度爲100%,三個td,第1個和第3個固定寬度爲100px,那麼中間那個就會自適應了,下面是一個實時的demo:html
left | middle | right |
可是table佈局是不推薦的,table佈局是css流行以前使用的佈局,有不少缺點:當table加載完以前,整個table的都是空白的,table將數據和排版參和在一塊兒,使得頁面混亂,而且table佈局修改排版十分麻煩和困難。編程
若是不用table佈局,那麼第二個想到的辦法是採用float,讓左邊的div float left,右邊的div float right,以下邊所示:瀏覽器
Action 1 先讓左右兩個div浮動ide
中間還有一個div,若是將中間的div排在第二:佈局
<div style="float:left;">left</div> <div>middle</div> <div style="float:right;">right</div>
那麼效果是這樣的:post
Action 2 右邊的div浮動到了第二行flex
由於div默認的display爲block,若是不設置width的話,塊級元素會盡量多地佔用水平空間。若是設置了width: 200px,效果是這樣的:ui
Action 3 右邊的div仍然浮動到了第二行flexbox
第三個div仍然會換行,由於float right會排到當前行儘量右邊的位置,即它的容器盒的邊緣或者挨着的上一個float的元素,若是當前行沒有空間的話,會不斷地往下移,直到有足夠的空間。因爲middle是一個塊盒,即便設置了width,當前行的空間也會被佔用,因此right只能到下一行纔有空間。
同時注意到middle雖然設置了200px,可是看起來和left同樣是100px寬了。這是由於float了的元素雖然在正常的文檔流以內,可是隻是讓相鄰(非float)的元素的內容圍繞着它排列,它仍然佔據相鄰元素的background和border空間。若是給middle添加一個白色的border,那麼看起來是這樣的:
Action 4 浮動的元素佔據了文檔流相應元素的背景和邊緣空間
明顯看到,float left的元素佔據了middle的background和border的空間,同時middle的內容圍繞着left排列。理解這點很重要。
假設middle裏面有個p標籤,而p標籤的內容比較長,那麼圍繞的效果是這樣的:
Action 5 浮動的環繞效果
環繞的元素一旦超出float元素高度以後,會以正常的寬度顯示
正如上面的註釋同樣,在float元素的那一行,相鄰的元素的內容的寬度將會縮短,以適應float元素佔去的寬度,而一旦超過float元素的區域,相鄰元素的內容顯示寬度就會正常。
因爲默認的div會佔一行,因此不能將middle放在第二個div,得放到第三個div。把第二個div和第三個div換一下順序:
<div style="float:left;">left</div> <div style="float:right;">right</div> <div>middle</div>
先讓float right的div渲染,再渲染middle的div。由於渲染left以後,left的那一行仍然有空間,這是因爲float left以後,只會佔據當前行的background和border,而當前行還有很大的空間,因而第二步渲染right時就和left同一行了,效果:
Action 6 先渲染左右兩個div,再渲染中間的div
若是不設置middle的width,那麼middle將圍繞着left和right環繞,和left同樣,right也會佔用middle的空間。
Action 7 中間的div圍繞着左右的div環繞
爲了讓middle和left/right中間有一個margin值,設置下middle的左右margin各爲110px,這樣就和左右和中間就各有10px的間距:
Action 8 設置中間div的margin值爲100px + 10px
這種辦法的優勢是實現簡單,支持性好。
這種自適應寬度的原理是利用了float的圍繞特性,佔據天然文檔流的background/border位置。這個圍繞特性不只會影響當前行的內容,還會影響下一行的內容,以下說明:
<p>第一段內容,略<img src="" style="float:left;"></img></p> <p>第二段內容,略</p>
Action 8 float元素佔據了下一行的空間
第一段落圍繞着圖片排列
圖片的float屬性也影響了第二段落,也就是說float會佔據天然文本流相應位置元素的背景和邊框,即便和float的元素不在同一行
網上還有一種margin負值法。margin負值法的步驟是:第一步讓中間的middle佔100%的寬度,而middle的內容設置左右margin各爲100px,這樣就實現了middle居中自適應寬度的效果:
<div style="width:100%;"> <div style="margin: 0 100px;">middle</div> </div>
接下來讓left的margin-left值爲-100%,因爲這個比例是相對於瀏覽器窗口大小的,因此要是left和middle是在同一行的話,left就能夠跑到middle的最左邊。可是由於middle的容器盒是一個普通的div,會佔據一整行,left就會排到下一行,這個時候設置margin-left爲一個負數時就跑出屏幕了。因此讓middle float一下,left就會排到第一行最左邊,同時middle覆蓋在上面:
<div style="width:100%; float:left;"> <div style="margin: 0 100px;">middle</div> </div> <div style="margin-left: 0">left</div>
從上面能夠看到:這樣實現,致使left的內容被擠出目標區域,由於正如上面所說,middle佔據了left的背景空間,上面的狀況是把它佔滿了,left內容只能overflow了。因此這樣實現是有問題的,得讓left也向左float一下,這樣left就緊挨在middle後面了,因爲middle佔了100%的寬度,因此再讓left向左邊margin了-100%後,left就恰好在最左邊了。
<div style="width:100%; float:left;"> <div style="margin: 0 100px;">middle</div> </div> <div style="float: left; margin-left: -100%;">left</div>
注意這裏,雖然left float以後看起來也是被排到下一行了,但和默認的div獨佔一行是不同的。float以後的left仍然和middle是同一行的,由於空間不足的時候,float只是把當前行盒的空間撐大,就和一個div塊盒裏面有不少個display爲inline-block的行內級盒是一樣的道理。例如:
<style> button{ width: 150px; } </style> <div style="width: 300px;"> <button>按鈕1</button> <button>按鈕2</button> <button style="margin-left: -200px;">按鈕3</button> </div>
按鈕3設置了一個很大的margin-left的負值後並非跑到屏幕外了,而是在和其它兩個按鈕同一行的位置,顯示以下:
注意,設置了float的元素並非把display改爲了 inline-block,大部份display的css計算值都變成了block,同時對本來是display: flex的沒有改變:
指定值 | 計算值 |
---|---|
inline |
block |
inline-block |
block |
inline-table |
table |
table-row |
block |
table-row-group |
block |
table-column |
block |
table-column-group |
block |
table-cell |
block |
table-caption |
block |
table-header-group |
block |
table-footer-group |
block |
flex |
flex , but float has no effect on such elements |
inline-flex |
inline-flex , but float has no effect on such elements |
other | unchanged |
來自MDN
由上表可看出,一個span設置了float: left/right以後,就不須要再設置成display: block/inline-block了,直接設置寬高便可。
迴歸正題,left的div設置了margin-left: -100%以後就跑到左邊去了,而right也是一樣道理,將right的margin-left相應地設置爲-100px,就跑到最右邊去了:
<div style="width:100%; float:left;"> <div style="margin: 0 100px;">middle</div> </div> <div style="margin-left: -100%;">left</div> <div style="margin-right: -100px;">right</div>
Action 9 margin負值法
下面討論第三種方法,使用display: table-cell
因爲table的展現擁有自適應的特色,所以把須要自適應寬度的middle的display屬性設置爲table-cell。
<div style="float:left;">left</div> <div style="float:right;">right</div> <div style="display:table-cell;">middle</div>
效果以下:
發現table-cell的寬度是根據內容自適應的,這裏是要根據瀏覽器窗口自適應,所以給middle添加一個很大的width就能夠了,例如width:2000px:
因爲ie6/7不支持display: table-cell,因此若是要支持ie6/7的話,就得用display: inline-block,ie6/7的inline-block和標準不同,它是用來觸發hasLayout特性,使元素擁有佈局屬性。做用在行內元素,可使得寬高等設置生效,若是做用在塊元素,僅是觸發佈局特性,還要再設置成inline纔是行內塊元素,若是不設置inline效果就跟table-cell很像。不同的地方是:設置了width:2000px,致使太長會換行,所以得用ie6/7的hack,設置*width: auto從新改會width值就能夠了:
<style> .middle{ display: table-cell; *display: inline-block; /* _和*開頭的只有ie6/7會識別 */ width: 2000px; *width: auto; } </style> <div style="float:left;">left</div> <div style="float:right;">right</div> <div class="middle">middle</div>
可是筆者認爲:爲了互聯網的美好,不要再兼容ie6/7了,甚至ie8。
接下來,繼續第四種方法,使用flex佈局,十分簡單:只須要將容器設置爲display: flex,而後再設置middle的flex-grow爲1便可:
<section style="display:flex;"></section> <div>left</div> <!--寬度爲100,省略--> <div style="flex-grow: 1;">middle</div> <div>right</div>
Action 11 使用flex-grow自適應寬度
flex-grow: 1的做用是把middle的寬度置爲flex容器的剩餘寬度,就達到了自適應的目的。flex的使用不做全面介紹,詳情可查看CSS-Tricks: A Complete Guide to Flexbox
可是flex佈局ie的支持性較差,具體查看caniuse.
最後再分析另一個自適應的例子,某個元素的寬度要根據其它元素的寬度自適應。以下圖所示,排名的位數變化可能會很大,致使最右邊的文字要自適應:
根據上面的一番分析,這個例子就不難實現了,以下面的分析,p標籤裏的文字寬度就能自適應了:
<div style="width:320px;"> <span style="width:14px;float:left;">排名</span> <span style="font-size:40px;float:left;">89</span>
<img style="width:44px;height:44px;float:left;" src="..."></img> <p>你的好友會編程的銀豬在土壕榜中排名89</p> </div>
實際效果:
你的好友會編程的銀豬在土壕榜中排名1
你的好友會編程的銀豬在土壕榜中排名6783
使用float是最簡單的,還能夠嘗試使用flex佈局,主要用到flex-shrink屬性,flex-shrink的做用是定義收縮比例,容器內的子元素的寬度和若超出容器的寬度時,將按比例收縮子元素的寬度,使得寬度和等於容器的寬度。以下所示,將前面三個span/img的flex-shrink設置爲0,而p的flex-shrink設置爲1,這樣子使得溢出的寬度都在p標籤減去,就可以達到p標籤寬度自適應的效果。
<style> span,img{ flex-shrink: 0; } p{ flex-shrink: 1; } </style> <div style="display:flex;width:320px;"> <span style="width:14px;">排名</span> <span style="font-size:40px;line-height:45px;">89</span> <img style="width:44px;height:44px;" src="..."></img> <p>你的好友會編程的銀豬在土壕榜中排名89</p> </div>
實時效果:
你的好友會編程的銀豬在土壕榜中排名89
你的好友會編程的銀豬在土壕榜中排名1890
上文綜合分析了最原始的table佈局,而後就是float佈局、table-cell、margin負值法、以及flex佈局來實現自適應寬度的實現和原理,重點討論了float的一些特性。若是上面的分析有錯誤,還望指正。
我的博客: http://yincheng.site/css-layout
參考:
1. CSS Float Theory: Things You Should Know
2. CSS Tricks: All About Floats
5. 視覺格式化模型