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


      如何實現一個三欄自適應布局,左右各100px,中間隨着瀏覽器寬度自適應?

      第一個想到的是使用table布局,設置table的寬度為100%,三個td,第1個和第3個固定寬度為100px,那么中間那個就會自適應了,下面是一個實時的demo:

left  middle  right 

      但是table布局是不推薦的,table布局是css流行之前使用的布局,有很多缺點:當table加載完之前,整個table的都是空白的,table將數據和排版參和在一起,使得頁面混亂,並且table布局修改排版十分麻煩和困難。

      如果不用table布局,那么第二個想到的辦法是采用float,讓左邊的div float left,右邊的div float right,如下邊所示:

Action 1 先讓左右兩個div浮動

float left 
float right 

      中間還有一個div,如果將中間的div排在第二:

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

     那么效果是這樣的:

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

left 
middle 
right 

      因為div默認的display為block,如果不設置width的話,塊級元素會盡可能多地占用水平空間。如果設置了width: 200px,效果是這樣的:

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

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. 視覺格式化模型

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM