CSS布局小結

前言

因為要在團隊內作一次技術分享,想了幾個題目,最后還是決定系統總結一下我在CSS布局這方面的知識。一是這個題目不算艱深,比較符合目前我的水平,同時也方便團隊的設計和其他偶爾幫忙切切頁面的小伙伴理解;二是從入行以后學得就比較雜亂,長此以往也不是好事,今年希望好好總結沉淀一下學到東西;三是剛剛過去的2016年正好是CSS正式誕生20周年(1996年12月17日,CSS第一版標準正式發布),我的首次技術分享取這個題目也有紀念意義。另外,準備分享的那天同時穿上第三屆CSS CONF送的T恤,嘿嘿。


先來說說過去和現在的常見布局吧

  • 古老的table布局
    記得我在大學時候,有一門課程叫網頁設計和網站制作,學的就是HTML和CSS來制作一些頁面,用的還是Dreamweaver來寫代碼,那時老師課堂上教的就是table布局。當時的我怎么也不會想到幾年之后會靠一行吃飯,人生真是充滿了戲劇性。
    作為十多年前的主要布局方案,table布局的存在也是有一定道理的。table系列標簽天生自帶的柵格、伸縮和內容垂直居中等特性能很方便地解決布局排版的許多痛點,特別是在那個CSS還不夠強大,或者說更多深入的CSS運用還沒有被人挖掘出來的年代。不過這個布局方案的缺點也是顯而易見的:完全背離HTML標簽的語義;標簽冗余;擴展性和自由度不高。

  • DIV和CSS時代
    從這時起,網頁的布局算是正式進入現代了。div標簽全稱的單詞是division,顧名思義,每一個div就是一個分隔出來獨立的區域,它和CSS中floatpositiondisplay等屬性的結合,在今天仍然是我們布局排版的主要手段。甚至在五六年前吧,市面上有很多這方面的技術書取名就用了DIV和CSS,搞得好像DIV是某一個專門的技術似的(想起半月前知乎那個SUV+CSS的梗了,哈哈哈哈),其實就是HTML里的一個塊級標簽,業界都拿它來用想必也是因為它的默認塊級屬性和其本身是唯一不帶任何自身樣式,最為“干凈”的塊級標簽吧。

    目前主流的基于DIV和CSS的布局,主要利用的也是CSS(CSS2)中的三塊核心內容:浮動、定位和盒子模型。關于這些內容網上的教程已經講得很清楚了,下面主要是思路介紹和一些個人覺得需要注意的地方。

    比如我們利用左右浮動,可以讓元素脫離原本的文檔流,貼到父級元素的最左或者最右邊,當然這中間可能會有需要清除浮動的情況,另外值得注意的是:其一,浮動后的元素會脫離文檔流,平面看上去雖然還是在一層,然而垂直方向上已經高于相鄰的其他元素了;其二,浮動屬性最初設計是為了實現文字圖片環繞的排版需求的,因此,個人覺得開始用它作為塊級元素的排版本身也是對屬性利用的深度挖掘吧。

    利用定位屬性,我們可以隨心所欲地將目標元素放在參照物內的任何地方,然而如果定位真有這么完美又怎么可能會出現那么多其他的布局方案呢?就拿絕對定位來說吧,我們定位排好一個元素之后,試著縮小瀏覽器的寬度到一定程度,定位元素就會和相鄰元素發生重疊的情況。此外,關于絕對定位的參照物問題也需要說明,過去有很多資料(甚至包括w3cschool)指出絕對定位的參照元素是從absolute開始,向上一直追溯到定位屬性不是默認的static的元素,如果都沒有,那就以<html>,有的也說是<body>為參照。其實關于這一點在官方標準里說得很明確:
    <blockquote>
    The position and size of an element's box(es) are sometimes calculated relative to a certain rectangle, called the containing block of the element.

    If the element has 'position: absolute', the containing block is established by the nearest ancestor with a 'position' of 'absolute', 'relative'or 'fixed', in the following way: In the case that the ancestor is an inline element, the containing block is the bounding box around the padding boxes of the first and the last inline boxes generated for that element. In CSS 2.1, if the inline element is split across multiple lines, the containing block is undefined. Otherwise, the containing block is formed by the padding edge of the ancestor.

    If there is no such ancestor, the containing block is the initial containing block.

關于initial containing block :The containing block in which the root element lives is a rectangle called the initial containing block. For continuous media, it has the dimensions of the viewport and is anchored at the canvas origin...
</blockquote>
簡而言之,絕對定位的參照元素是其最近的具有定位屬性為absolute/relative/fixed三者之一的祖先元素的包含塊來定位;如果沒有,則追溯到根元素的包含塊,即初始包含塊(這里不是根元素html,我們可以給html設一個寬高進行實驗,發現定位的元素還是相對于整個窗口而言的),這個初始包含塊可以簡單理解為視窗形成的矩形,其原點是左上角(canvas origin)。再啰嗦一點關于相對定位和絕對定位的區別,相對定位的元素依然會進入文檔流,會保留它在文檔流中的占位,而絕對定位的元素則會脫離文檔流(這也是為什么我們通常選擇給父元素設置relative來作為子元素絕對定位的參照,因為不給它任何偏移量的話它依然還是原來在文檔流中的位置)。

至于盒子模型,則主要是利用內外邊距marginpadding來進行局部調整布局了。需要注意的就是盒模型寬高的計算,還有IE和非IE瀏覽器下盒模型渲染的差異,以及相鄰元素margin重合的情況,當然也記得使用負邊距這個腦洞(雖然似乎官方并不推薦使用負值,border-radius負值渲染為內向圓角的提案就曾被駁回了=_=)。

  • 圣杯和雙飛翼布局
    基于上面提到的CSS屬性的利用,前輩們創造出了兩欄,三欄,定寬+自適應等各種組合的頁面布局方案,這里主要選擇兩個最具代表性的布局來作為示例。

    圣杯布局(Holy Grail)其實就是左右兩列定寬,中間內容自適應三列布局的一種算是完美的實現。過去的三欄布局基于文檔流的順序,需要leftmainright三列依次渲染,而圣杯布局則解決了這一問題,它可以:
    <blockquote>
    兩邊帶有固定寬度中間可以流動伸縮(fluid)
    允許中間一欄最先出現在文檔中
    僅需一個額外的div標簽
    僅需非常簡單的 CSS,兼容性良好
    </blockquote>

    下面是主要代碼:

      /* HTML */
      // 可以看到只多了一個額外的container,并且主要內容main如愿放在了文檔流的前面
      <div class="header">頭部信息</div>
      <div class="container">
          <div class="main col">主要內容</div>
          <div class="left col">左側內容</div>
          <div class="right col">右側內容</div>
      </div>
      <div class="footer">底部信息</div>
      /* CSS */
          .header, .footer {
          background: #ccc;
          color: white;
          text-align: center;
      }
      .container {
          padding: 0 100px 0 200px; /* 設置container的左右內邊距為左右兩欄的寬度值 */
          color: white;
          text-align: center;
      }
      .container .col {
          float: left;  /* 為三列都設置左浮動,此時左右兩列還是被中列擠在下方 */
          position: relative; /* 都設置相對定位 */
      }
      .main {
          width: 100%; /* 中間寬度設置百分比 */
          background: green;
      } 
      .left {
          left: -200px;  /* 其實也可以是right: 200px; 移到左邊占據padding的位置 */ 
          width: 200px;
          margin-left: -100%;  /* 左側負margin100%,這個100%是container的寬度,此時到了上方 */ 
          background: red;
      }
      .right {
          width: 100px;
          margin-right: -100px; /* 移到右邊占據右側padding的位置 */ 
          background: blue;
      }
      .footer {
          clear: both; /* 清除footer周圍的浮動,避免上移 */
      }
      /* 需要注意的是沒有清除三列的浮動,container實際上是沒有包住三列的
         當然清除三列的浮動也可以實現同樣的效果 */
    

    雙飛翼布局則是根據圣杯布局改變而來,在原本的基礎上多增加了一層div嵌套,而省去了相對定位的設置,并且可以自由地利用margin屬性給中列設置左右的間隙,下面是主要代碼:

      /* HTML */
      // 原本main層的內容多用一個content層嵌套
      <div class="header">頭部信息</div>
      <div class="container">
          <div class="main">
              <div class="content">主要內容</div>
          </div>
          <div class="left">左側內容</div>
          <div class="right">右側內容</div>
      </div>
      <div class="footer">底部信息</div>
      /* CSS */
      .header, .footer {
          background: #ccc;
          color: white;
          text-align: center;
      }
      .container {
          /* 不再需要原本container的內邊距 */
          color: white;
          text-align: center;
      }
      .main {
          float: left;
          width: 100%;
      }
      .main .content {
          /* 利用content的外邊距來預留左右空間,并且多出來的數值會成為間隙 */
          margin: 0 220px 0 320px; 
          background: red;
      }
      .left {
          float: left;
          width: 300px;
          margin-left: -100%;
          background: green; 
      }
      .right {
          float: left;
          width: 200px;
          margin-left: -200px;
          background: blue;
      }
      .footer {
          clear: both;
      }
    

    其實對于圣杯和雙飛翼布局,我們還可以在這個基礎上進行發揮修改,比如兩列自適應單列定寬,比如左側自適應或者右側自適應等等,有興趣的同學可以自己試試。

  • 瀑布流
    所謂瀑布流,就是一列列長條狀的高度不一的盒子縱向排列,再配上JS做出下滑動態更新添加的效果,看上去像是瀑布一樣。關于瀑布流布局我也沒有在實際項目中運用到,大致了解通常是需要利用JS來計算列的數量和位置。不過CSS3提供的多列布局屬性能讓我們更加方便地實現這個效果,當然,方便背后的代價往往就是兼容性問題,不過除了IE,主流瀏覽器的支持情況已經很不錯了。
    <a >點擊這里</a>可以簡單看看CSS3多列屬性的介紹,下面是示例代碼:

    /* HTML */
    <div class="main">
        <div class="pic pic1">
            <h3>111</h3>
        </div>
        <div class="pic pic2">
            <h3>222</h3>
        </div>
        <div class="pic pic3">
            <h3>333</h3>
        </div>
        <div class="pic pic4">
            <h3>444</h3> 
        </div>
        <div class="pic pic5">
            <h3>555</h3>
        </div>
        <div class="pic pic6">
            <h3>666</h3>
        </div>
        <div class="pic pic7">
            <h3>777</h3>
        </div>
        <div class="pic pic8">
            <h3>888</h3>
        </div>
        <div class="pic pic9">
            <h3>999</h3>
        </div>
        <div class="pic pic10">
            <h3>1010</h3>
        </div>
    </div>
    /* CSS */
    * { 
        padding: 0;
        margin: 0;
     }
    .main {
        width: 1200px;
        margin: 60px auto;
        padding: 20px;
        box-shadow: 2px 2px 6px #ccc;
        column-width: 218px; /* 大盒子的寬度除以列寬會計算出有多少列 */
        column-count: 5; /* 也可以自己指定列數 */
        column-gap: 5px; /* 列間距其實自己也會計算 */
    }
    .main .pic {
        display: inline-block; /* 加上這個屬性讓每一塊不錯位 */
        width: 188px;
        min-height: 100px;
        padding: 0px 15px;
        margin: 10px 0;
        box-shadow: 2px 2px 6px #ccc;
    }
    .main .pic h3 {
        line-height: 100px;
        text-align: center;
    }
    /*給一些高度加以區分*/
    .pic1, .pic3 {
        height: 120px;
    }
    .pic2 {
        height: 180px;
    }
    .pic5 {
        height: 160px;
    }
    .pic7, .pic10 {
        height: 130px;
    }
    .pic8 {
        height: 150px;
    }
    
最終實現的效果
  • 利用display的其他布局辦法
    CSS的display屬性還為我們提供了更多的布局辦法,比如利用inline-block的行內特性實現多行元素在不同容器寬度下顯示不同個數的效果,然而痛點是解決左右兩側不居中的問題;再有比如display: tabledisplay: table-cell利用表格實現垂直居中,并且支持文本內容縱向擴展為多行的效果。下面則是個人認為未來將成為主流的布局方案。

承前啟后的彈性盒子display: flex

對于這個屬性,前后出現過幾次候選標準,所以網上搜索的時候往往會出來很多不同的屬性名稱,可能會讓人感到困惑,同樣它的兼容性問題在現在仍然是一個很煩人的事,明明這么方便的屬性卻不能痛快地使用。彈性盒子的教程我推薦兩個,看過之后其實已經完全可以上手用起來了,今后如果我有更多的使用心得也會再做筆記:

<a >這個網站除了flexbox也有講其他布局的基礎知識,非常推薦</a>
<a >阮一峰flex布局教程:語法篇</a>
<a >阮一峰flex布局教程:實戰篇</a>
阮一峰的教程已經將這個屬性講得足夠透徹了,實戰部分也為我們提供不少布局案例的flex實現方法,這里我也就不再贅述。關鍵就是理解主軸和縱軸的概念。

即將支持的新標準 CSS3 Grid Layout

display: grid這個屬性,我是在第三屆中國CSS CONF上通過大漠老師的介紹了解到的,目前已經成為候選標準,而據大會主持人透露,似乎今年(2017)各大主流瀏覽器就將全面支持該屬性,更可喜的是這個標準是由微軟提出的,而且早已率先在IE10實現了。( ⊙o⊙ )

<a >W3C網站上</a>有對于該屬性詳細的介紹和教程,雖然目前幾乎不太可能用于實際的項目,但還是列在這里也是希望能夠對這個新的技術方案保持關注。

就我在大會上看到的演示的直觀感受來說,這個屬性的強大完全不亞于display: flex,并且作為網格布局,比flexbox更適合作為頁面大結構的布局方案,而flexbox更適合盒子內容的排版,相信正如大漠老師所看好的,今后二者的結合或許會成為我們主流的布局手段。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,776評論 1 92
  • 1標準的CSS的盒子模型?與低版本IE的盒子模型有什么不同的? CSS盒子模型:由四個屬性組成的外邊距(margi...
    秦小婕閱讀 1,205評論 0 1
  • ?前端面試題匯總 一、HTML和CSS 21 你做的頁面在哪些流覽器測試過?這些瀏覽器的內核分別是什么? ...
    Simon_s閱讀 2,225評論 0 8
  • 首先帶著書中的問題去閱讀。 問題:第一, 為什么有些學習方式更有效,而有些卻不行? 第二,想讓職場工作者快速增加能...
    徐美美惠閱讀 538評論 0 2
  • 1.從我做起 改變,從我做起。如果每個人都在等著別人先開始,那就不會有任何的改變發生了。 2.非暴力溝通 先說具體...
    宵汀閱讀 459評論 1 1