CSS朝花夕拾之兩欄和三欄佈局

Web前端面試中遇到CSS的問題,難免會提到兩欄或三欄佈局,之前這是筆者的痛處,看過很多關於佈局的文章,但總感覺不得其精要,現在秋招來臨,不管怎麼樣,都要把之前遺留下的問題解決一下,於是有了這篇文章,與君共勉。

兩欄佈局和三欄佈局最常見的就是左邊或右邊或兩邊都是固定的寬度,而中間欄自適應,下面就着重講這方面的佈局。

一、兩欄佈局(左邊固定,右邊自適應)

初始
自適應(因爲圖片縮放緣故,看起來兩張圖aside寬度不一樣,其實是一樣的)

  1. 假如html與css初始代碼如下,該如何實現該佈局呢?

    <div class="container" id="col-2-m*">
      <aside class="aside"></aside>
      <main class="main"></main>
    </div>
    *{
      padding:0;
      margin:0;
    }
    .container{
      background-color:#ccc;
    }
    .aside{
      width:400px;
      height:400px;
      background-color:#fefedd;
    }
    .main{
      background-color:#99fefe;
      height:400px;
    }

    • 利用float+margin佈局:

       #col-2-m1 .aside{
        float:left;
       }
       #col-2-m1 .main{
        margin-left:400px;
       }

      說明:將.aside設爲左浮動,它就會浮動在.container的左邊;然後爲.main設置左外邊距爲側邊欄的寬度即可。我們知道float元素會脫離文檔流,如果不設置.main的左外邊距的話,.main子元素可能會覆蓋到側邊欄。比如就文本來說,文本會圍繞浮動元素,若.aside的高度不爲.container的高度,.main文本會流到.aside下面。

    • 利用float+bfc佈局

        #col-2-m2 .aside{
          float:left;
        }
        #col-2-m2 .main{
          overflow:auto;/*overflow不爲visible就可以得到bfc*/
        }

      說明:另一種可使用的方法是利用bfc(塊級格式上下文)的性質,關於bfc,我會在下一篇文章中講解。當overflow不爲visible就可以創建一個bfc,bfc的區域不與float box重疊,所以滿足要求。

    總結:如果你細心的話,可以發現我剛介紹的這兩種佈局的html結構順序爲側邊欄在main的上面,也就是當我們得到頁面時會先渲染側邊欄,其實我們大多數時候都是要先渲染main欄的,因爲側邊欄包含的一般都是不重要的東西,而主欄卻是重要的東西。所以這中結構不夠實用。不過這種結構的好處是佈局方便簡單。

  2. 好了,假如是以下html結構呢,該怎麼做呢?

        <div class="container" id="col-2-m*">
          <main class="main"></main>
          <aside class="aside"></aside>
        </div>

    上面的這種結構是大部分兩欄佈局採用的結構,佈局方法有很多也很雜,我們慢慢來。


    • 利用margin+position佈局

        #col-2-m3{
          position:relative;
        }
        #col-2-m3 .aside{
          top:0;
          left:0;
          position:absolute;
        }
        #col-2-m3 .main{
          margin-left:400px
        }

      說明:這種方法其實無關html結構,即.aside和.main誰先誰後都沒問題,因爲.aside是絕對定位在.container的左端的。只不過需要注意爲父元素設置相對定位方式。需要注意的是,如果中間欄含有最小寬度限制,或是含有寬度的內部元素,則瀏覽器窗口小到一定程度,主面板與側欄會發生重疊,特別是對三欄佈局來說。

    • 利用padding+負margin+float+postition佈局

        #col-2-m4{
          padding-left:400px;
          box-sizing:border-box;
        }
        #col-2-m4 .aside{
          float:left;
          margin-left:-100%;
          position:relative;
          left:-400px;
        }
        #col-2-m4 .main{
          float:left;
          width:100%;
        }

      分析:此爲經典的聖盃佈局。該種佈局方式初看挺複雜的,其實仔細想想也就釋然了(我也就想了好幾天吧)。它與之前一個很大的不同的地方是.aside和.main都成了左浮動,而且用到了負外邊距。.main沒有用到margin-left,取而代之的是父元素設置了padding-left,padding-left也是爲.aside留的區域。而.main寬度又是100%,所以.aside會被擠到下一行去,在下一行什麼位置那?在main的正下方。要把.aside拉上去,使用margin-left:-100%可以做到,此時.aside就變成了同.main的左部完全對齊,但要把它變爲和.container左邊對齊,所以要設置一個相對定位,left爲負的它的寬度,這樣設置後就完成了兩欄佈局。其中我爲.container設置了box-sizing:border-box;是爲了去掉水平方向的滾動條。因爲讓.main的寬度爲100%(即.container的寬度也是瀏覽器窗口的寬度)再加上父盒子的左內邊距就會使整個佈局寬度大於瀏覽器窗口的寬度了,設置爲border-box實際上就會使width=瀏覽器窗口寬度-左內邊距,不會發生溢出。

    注意:聖盃佈局有個很大的缺點是,當.main的寬度小於.aside的寬度時佈局就會發生錯亂,就本例子而言,你可以試試將瀏覽器窗口寬度縮小到800px及一下就可以看到這種現象。這是因爲將.aside拉到上一行時,上一行的寬度不足以支撐整個.aside,所以造成了佈局混亂。如下圖所示:
    佈局發生了混亂
    解決方法可以是爲body設置min-width爲大於2倍.aside的寬度,或使用其他方式的佈局。

  3. 感覺怎麼樣了?是不是漸入佳境了?接下來思考一下以下html結構:

    <div class="container" id="col-2-m5">
        <div class="main-wrap">
          <main class="main"></main>
        </div>
        <aside class="aside"></aside>
    </div>

    該種形式的html結構在.main的外圍包裹了一層div,這種方法叫雙飛翼佈局,實際上是對上面聖盃佈局的改進。該怎麼做?


    • 利用負margin+float+margin佈局

        #col-2-m5 .aside{
          float:left;
          margin-left:-100%;
        }
        #col-2-m5 .main-wrap{
          float:left;
          width:100%;
        }
        #col-2-m5 .main{
          margin-left:400px;
        }

      分析:此爲雙飛翼佈局,它和聖盃佈局相同點是對子盒子都用了浮動定位,不過它對於聖盃佈局有了幾點的改進,而且它解決了聖盃佈局主欄寬度小於側邊欄寬度時的佈局錯亂問題。用了一層div包裹.main,它的好處是不需要.container使用padding-left了,而且.aside沒必要相對定位了。爲什麼這麼說呢?首先針對前者,因爲在.main上包裹一層,所以可以直接使用.main的margin-left來實現留出固定寬度的區域給.aside,而.main-wrap可以直接設置寬度爲100%;而因爲.main-wrap和.container左邊界是對齊的,.aside設置margin-left:100%也就直接到了上層的左邊界,每必要再做相對偏移了。

    注意:其實對於所有的負margin實現把.aside拉到上面一層去,都可用絕對定位來實現的。它們可以相互替換,自己做佈局時不要拘泥於這些,只要做出來就好了。

  4. 最簡單的方法要來了,相信你也想到了,那就是flex佈局了。
    針對上面的結構1、2,我們可以這樣來實現:

      #col-2-m6{
        display:flex;
        /*針對.aside在.main下面的html結構順序還需加入以下語句:
        flex-direction:row-reverse;
        */
      }
      #col-2-m6 .main{
        flex:1;
      }

    注意:因爲flex佈局默認按html結構順序從左到右佈局,所以針對.aside在.main下面的結構,爲避免側邊欄在右邊,還要加個flex-direction:row-reverse;,以使側邊欄恢復到左側。

二、 三欄佈局(左右固定,中間自適應)

如果你已經深入理解了兩欄佈局,三欄佈局自然手到擒來了。三欄佈局相對於二欄佈局來說,相應的外邊距和內邊距都有加上右內邊距了;而對於負外邊距來說,右邊欄的負外邊距要設置爲負的自身的寬度,以使其能準確靠在右側。
三欄佈局效果圖

下面是我的代碼,僅供參考:

<h3>三列布局,一列自適應,兩列固定寬度 margin+float</h3>
   <div class="container col-3-m1">
     <aside class="aside">aside1</aside>
     <aside class="extra">aside2</aside>
     <main class="main">main</main>
   </div>
   <h3>三列布局,一列自適應,兩列固定寬度聖盃佈局 負margin+float+padding+position</h3>
   <div class="container col-3-m2">
     <main class="main">main</main>
     <aside class="aside">aside1</aside>
     <aside class="extra">aside2</aside> 
   </div>
   <h3>三列布局,一列自適應,兩列固定寬度雙飛翼佈局 負margin+float+margin</h3>
   <div class="container col-3-m3">
     <div class="main-wrap">
       <main class="main">main</main>
     </div>
     <aside class="aside">aside1</aside>
     <aside class="extra">aside2</aside> 
   </div>
   <h3>三列布局,一列自適應,兩列固定寬度 flex佈局</h3>
   <div class="container col-3-m4">
     <aside class="aside">aside1</aside>
     <main class="main">main</main>
     <aside class="extra">aside2</aside> 
   </div>
   <h3>三列布局,一列自適應,兩列固定寬度 float+position</h3>
   <div class="container col-3-m5">
     <aside class="aside">aside1</aside>
     <main class="main">main</main>
     <aside class="extra">aside2</aside> 
   </div>
*{
  padding:0;
  margin:0;
  *box-sizing:border-box;
}
h3{
  text-align:center;
  margin:5px 0;
}
.container{
  height:500px;
  *border:2px solid red;
  position:relative;
}
.aside{
  width:150px;
  height:100%;
  *border:2px solid blue;
  background-color:#88ff88;  
}
.extra{
  width:200px;
  height:100%;
  *border:2px solid green;
  background-color:#88f;  
}
.main{
  height:100%;
  *border:2px solid orange;
  background-color:#ff8888; 
}
.col-3-m1 .aside{
  float:left;
}
.col-3-m1 .extra{
  float:right;
}
.col-3-m1 .main{
  margin-left:150px;
  margin-right:200px;
}
.col-3-m2{
  padding:0 200px 0 150px;
}
.col-3-m2 .aside{
  float:left;
  margin-left:-100%;
  position:relative;
  left:-150px;        
}
.col-3-m2 .extra{
  float:left;
  margin-left:-200px;
  position:relative;
  right:-200px;
}
.col-3-m2 .main{
  width:100%;
  float:left;
}
.col-3-m3 .aside{
  float:left;
  margin-left:-100%;    
}
.col-3-m3 .extra{
  float:left;
  margin-left:-200px;
}
.col-3-m3 .main-wrap{
  width:100%;
  float:left;
  height:100%;
}
.col-3-m3 .main{
  margin-left:150px;
  margin-right:200px;
}
.col-3-m4{
  display:flex;
}
.col-3-m4 .main{
  flex:1;
}
.col-3-m5 .aside{
  position:absolute;
  top:0;
  left:0;        
}
.col-3-m5 .extra{
  position:absolute;
  top:0;
  right:0;
}
.col-3-m5 .main{
  margin-left:150px;
  margin-right:200px;
}

參考資料:
CSS佈局十八般武藝都在這裏了

發佈了21 篇原創文章 · 獲贊 27 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章