從三欄自適應寬度佈局到css佈局的討論

      如何實現一個三欄自適應佈局,左右各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

float left 
float right 

      中間還有一個div,若是將中間的div排在第二:佈局

<div style="float:left;">left</div>
<div>middle</div>
<div style="float:right;">right</div>

     那麼效果是這樣的:post

Action 2 右邊的div浮動到了第二行flex

left 
middle 
right 

      由於div默認的display爲block,若是不設置width的話,塊級元素會盡量多地佔用水平空間。若是設置了width: 200px,效果是這樣的:ui

Action 3 右邊的div仍然浮動到了第二行flexbox

float left 
middle 
right 

      第三個div仍然會換行,由於float right會排到當前行儘量右邊的位置,即它的容器盒的邊緣或者挨着的上一個float的元素,若是當前行沒有空間的話,會不斷地往下移,直到有足夠的空間。因爲middle是一個塊盒,即便設置了width,當前行的空間也會被佔用,因此right只能到下一行纔有空間。

      同時注意到middle雖然設置了200px,可是看起來和left同樣是100px寬了。這是由於float了的元素雖然在正常的文檔流以內,可是隻是讓相鄰(非float)的元素的內容圍繞着它排列,它仍然佔據相鄰元素的background和border空間。若是給middle添加一個白色的border,那麼看起來是這樣的:

Action 4 浮動的元素佔據了文檔流相應元素的背景和邊緣空間

float left 
middle 
right 

       明顯看到,float left的元素佔據了middle的background和border的空間,同時middle的內容圍繞着left排列。理解這點很重要。

       假設middle裏面有個p標籤,而p標籤的內容比較長,那麼圍繞的效果是這樣的:

Action 5 浮動的環繞效果

float left 
middle 

環繞的元素一旦超出float元素高度以後,會以正常的寬度顯示

float right 

      正如上面的註釋同樣,在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

left 
right 
middle 

       若是不設置middle的width,那麼middle將圍繞着left和right環繞,和left同樣,right也會佔用middle的空間。

Action 7 中間的div圍繞着左右的div環繞

left 
right 
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ middle圍繞着left和right環繞,同時left和right佔據着middle的空間 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

       爲了讓middle和left/right中間有一個margin值,設置下middle的左右margin各爲110px,這樣就和左右和中間就各有10px的間距:

Action 8 設置中間div的margin值爲100px + 10px

left 
right 
middle 設置margin: 0 110px;

       這種辦法的優勢是實現簡單,支持性好。

       這種自適應寬度的原理是利用了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>
middle

       接下來讓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>
middle
left

      從上面能夠看到:這樣實現,致使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>
middle
left

       注意這裏,雖然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負值法

middle
left
right

 

      下面討論第三種方法,使用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>

      效果以下:

left
right
middle

      發現table-cell的寬度是根據內容自適應的,這裏是要根據瀏覽器窗口自適應,所以給middle添加一個很大的width就能夠了,例如width:2000px:

Action 10 讓中間的div使用table-cell自適應寬度
left
right
middle

      因爲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自適應寬度

left
middle flex-grow: 1
right

      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

你的好友會編程的銀豬在土壕榜中排名1

排名 6783

你的好友會編程的銀豬在土壕榜中排名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

你的好友會編程的銀豬在土壕榜中排名89

排名 1890

你的好友會編程的銀豬在土壕榜中排名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

3. CSS-Tricks: display

4. Understanding Floats

5. 視覺格式化模型

相關文章
相關標籤/搜索