當遇到css佈局,你在考慮什麼?

TTtuntuntutu發表於2019-04-02

​ CSS佈局在前端開發中像呼吸一樣——再平常不過的事。比如同事A在嚐到了Flexbox佈局的甜頭之後,任何佈局都會以display:flex打頭陣,同事B因為專案得支援IE10,像避開毒蛇一樣的避開Flexbox佈局方法。你會發現我可能有點嗤笑這樣的行為,我曾經也是這樣的一員,我想為這個問題——當遇到css佈局,你在考慮什麼? 整理一個完整的解決方案。

​ 你在考慮什麼:從什麼樣的HTML結構出發能夠幫助到css佈局?你的佈局方法武器庫都有什麼,在具體場景下,選擇什麼合適的佈局方法?需要做支援舊瀏覽器嗎?Flexbox、Grid這些佈局的方法弄潮兒在舊瀏覽器中的最佳實踐是?等等等等。

​ 本文將介紹我的"答案",歡迎胖友們補充、更正。

Normal flow:css佈局的起點

​ Normal flow(不知什麼中文翻譯妥帖,還是扔了英文...)指的是如果沒有改變css佈局程式碼,網頁中標籤的預設表現方式。比如demo-normal-flow:塊級標籤p挨個從上往下,而內聯標籤span表現得像段落中的文字。

​ 當我們建立、自定義一個佈局,其實是調整標籤在Normal flow中的位置,或是直接從Normal flow移除,我們最最原始的材料就是Normal flow。如果使用語義化標籤(semantic markup),從一個結構良好的HTML文件開始是很有幫助的:

  1. 語義化標籤確保**內容可讀,**即使是非常受限制的瀏覽器、像螢幕閱讀器這樣的裝置也如此;
  2. 以此為起點去佈局文件,是合作友好的,而不是破壞性的、改動很大的,因為大多數標籤還是在Normal flow中;

HTML5新加了些幫助結構化的標籤,html-document-structured 這篇文章可以參考,這裡做一個overview:

  • headerbodymain標籤的直接子標籤,位置在頁面頭部,內容可能為logo、標語、搜尋提示、導航欄;
  • nav:導航欄包在nav標籤內,可能出現在頭部、側邊欄、底部等等,這裡有個demo-mdn-nav,神奇的地方在於設定nav標籤的display:inline-block,是作用在li標籤上的;
  • mainbody標籤的直接子標籤,主內容區域;
  • aside:側邊欄;
  • article:一般出現在main標籤內,article標籤內可以有sectionfooter等標籤,是比較獨立的內容,比如像部落格網站主頁的一個文章簡介;
  • sectionsectiondiv很類似,如果使用div標籤是為了對內容做樣式控制,或者為了便於javascript獲取做其他操作,那麼使用div就是你的答案,其他情況就用section
  • address:提供聯絡資訊,放在article標籤內提供文章作者資訊,放在mainbodyfooter內提供網站資訊;
  • footer:一般在HTML結構底部,補充網站資訊,如果放在article內補充文章資訊;

Normal flow是CSS佈局的起點,更好的選擇是語義化標籤(semantic markup)作為CSS佈局的起點。

在具體場景下選擇合適的佈局方法

css佈局方法有很多,如Flexbox、Grid、Float等等等等,在使用之前得把握兩個中心思想

  1. 每種佈局方法有它的使用場景、使用上下文,在具體場景中選擇對應合適的佈局方法才是王道;
  2. 一個頁面往往會應用多種佈局方法,而不是一種佈局方法解決所有問題,佈局方法間是合作的關係;

接下來主要以講demo的形式介紹各個佈局方法的使用場景,對於佈局方法自身如何使用不會過多說明。

Flexbox

Flexbox是Flexible Box Layout的簡稱,Flexbox既可以用於整個頁面的佈局,也可以用於區域性部件的佈局。Flexbox存在些瀏覽器相容性的問題,在舊瀏覽器中的實踐會在之後說明。接下來幾個場景是建立在瀏覽器支援Flexbox的前提下。

Flexing sizing of flex items

Flexbox全稱Flexible Box Layout中的Flexible(靈活性),是它的立命之本。Flexbox的第一個使用場景也呼之欲出——Flexing sizing of flex items,也就是盒子尺寸的高度靈活性:

  1. demo-flexbox-flexsection標籤是Flex容器,article標籤是Flex item,其中前面兩個article標籤flex:1 200px,最後一個article標籤flex:2 200px。具體表現為,如果不能提供3個Flex item都是200px寬度的空間,則它們仨寬度一致,如果能提供,剩餘空間按照1:1:2分配;

當遇到css佈局,你在考慮什麼?

當遇到css佈局,你在考慮什麼?

  1. demo-flexbox-flex-fixedWidthWithFlex:這是實際使用中一個很常見的做法,這裡將footer標籤高度固定,section標籤因為flex:1而佔據餘下所有空間。在水平方向,也可以是側邊欄寬度固定,主要內容佔據餘下所有空間;

水平、垂直位置調整

Flexbox提供像align-itemsjustify-content這樣的屬性去調整flex items在主軸(main axis)、副軸(cross axis)的位置。比如最常見的考試題,水平垂直居中某個元素,demo-flexbox-alignment ;再比如justify-content:space-around 作用於導航條的樣式,demo-flexbox-alignment-justify-content

調整標籤順序

一般來說,標籤出現順序由原始碼中出現順序決定,Flexbox為Flex items提供了order屬性,提供從css角度調整Flex items在頁面中出現的順序的能力。

補充一個黑科技

如果為Flex item設定主軸方向(main axis)的margin值為auto,比如主軸是橫向的,設定margin-left:auto,這個Flex item會佔據往左這個方向的剩餘空間:demo-flex-flex-item-margin:auto

Grid

Grid佈局,和Flexbox設計為在一個方向佈局不同,它幫助我們更加容易地從兩個方向上佈局元素。我更加推薦Grid佈局應用於整個頁面,因為它非常清爽、優雅。它同樣存在瀏覽器相容問題,且比Flexbox更要重,在舊瀏覽器中的實踐會在之後說明。接下來幾個場景是建立在瀏覽器支援Grid的前提下。

優雅的整個頁面佈局

為什麼說它優雅呢?看幾個demo就知道了。

demo-grid-layoutdemo-grid-layout-grid-template-areas:兩個demo都實現了最基本的一個頁面情況,一個頭部、一個側邊欄、一個主要內容區域、一個底部,前者是Grid佈局最常規的使用,後者使用了grid-template-areas屬性;

另外在Grid佈局之前,有一些庫在做模擬Grid System的工作,將一個頁面分成6列或者12列,標籤按列去佔據頁面。Grid佈局方法完全有這樣一個能力,使用12列布局的Grid重寫前面兩個demo實現的效果:demo-grid "framework"

如果能使用Grid佈局整個頁面,我是強烈推薦的,它的思維切入點不再是一維,而是二維,這是一場變革。

Floats

Floats佈局方法既可以針對整個頁面,也可以針對區域性部件,雖然設計之初並不是為了佈局整個頁面。我是把Floats作為無法使用Grid、Flexbox時候的第一選擇。像前面提到的做Grid System的css庫,它其實也是將其中的每一個item設定為了float:left,然後計算佔據寬度的百分比以模擬Grid System。

另外,"floated item"(設定float:leftfloat:right)會從Normal flow中移除。來看看具體應用的demo吧。

文字環繞圖片

“文字環繞圖片”是Floats設計的初衷:demo-float-avatar image

文字首單詞首字母特殊處理

demo-float-a fun drop-cap effect

當遇到css佈局,你在考慮什麼?

頁面佈局:一個最常見Floats問題的解決

"Floated item"的高度是不包括在容器標籤內,如果高度超出容器標籤,會出現顯示上的錯誤,這是Floats應用於頁面佈局最常見的一個問題:demo-float-floated items overflow the wrapper

當遇到css佈局,你在考慮什麼?

解決方案有三種:

  1. demo-float-clearfix hack:在容器標籤偽類::after清除浮動,或者在容器標籤內加一個空的div元素清除浮動也可以解決問題;
  2. demo-float-overflow:使用overflow屬性建立一個BFC,但是小心overflow:hiddenoverflow:auto可能增加了你不需要的顯示效果;
  3. demo-float-display:flow-root:更現代的方法是使用display:flow-root建立一個BFC,而且不會像overflow增加不需要的顯示效果,但是得考慮瀏覽器支不支援這個屬性;

當遇到css佈局,你在考慮什麼?

Table layout

在許多年以前,web開發者使用table標籤做整個頁面的佈局,將頁面內容放入table的行和列中,這種方法的問題在於不靈活,而且語義錯誤(對於螢幕閱讀器的使用者很不友好)。之所以放入table標籤能佈局,是因為存在描述table layout的一些列css屬性,它們是和table這些標籤是繫結的。而直接使用這些css屬性,用於不是table這些元素佈局,這種方法被稱為是 "using CSS tables"demo-using css tables

"using css tables" 被稱作是一種遺留方法(legacy method),用於整個頁面佈局,適用於不支援Flexbox和Grid的瀏覽器,但是我這裡的最佳替補還是Floats。

Positioning

Positioning的定位和前面四種不太一樣,它一般不用於建立整個頁面佈局,而是管理和微調標籤,做一個區域性位置的調整。要注意如果已經設定以下幾個position屬性值的標籤,層級是高於Normal flow,層級可通過z-index屬性調整。

position:relative 相對定位,做位置調整

demo-positioning-relative-left/right:這個例子不是很深動形象,但是demo糙理不糙,確實是通過設定lefttop等屬性值去移動位置。

posision:absolute 絕對定位,做任何可彈出、可拖拽UI部件

MDN上放了這樣一個使用場景說明:

popup information boxes and control menus; rollover panels; UI features that can be dragged and dropped anywhere on the page; and so on...

postion:fixed 固定定位

demo-position-fixed:固定表頭,表頭位置始終定於頁面頂部,不隨滾動條滾動而滾動。

當然可用於任何需要固定於頁面某個位置的UI部件。

position:sticky 粘性定位

這裡有個很經典的例子: demo-sticky-a scrolling index page where different headings stick to the top of the page as they reach it ;但是在使用時得考慮瀏覽器相容問題,相容性目前堪憂。

Multicol

Multicol是Multi-columns layout的簡稱,它提供了一種在列中佈置內容的方法,類似於文字在報紙中的流動方式,使得閱讀更加友好,不用上下滾動。Multicol的定位是這一種特殊的內容展示佈局。

報紙閱讀模式

demo-multi-column layout:通過在container塊級元素上設定column-count或者column-width屬性開啟Multicol:

當遇到css佈局,你在考慮什麼?

Flexbox、Grid考慮支援舊瀏覽的最佳實踐

最初吸引我做這個話題的原因,是目前公司專案得支援IE10、IE11,現狀是專案中的佈局方法沒有Grid、鮮有Flexbox,就比較心癢癢,想搞搞明白到底能不能在支援IE10、IE11的情況使用這兩種潮流的佈局方法。所以在舊瀏覽器中的實踐重點考慮的是IE10、IE11兩位。

Flexbox: Postcss外掛Autoprefixer

瀏覽器對Flexbox的支援還是挺不錯的,IE10支援2012版語法,IE11支援的語法和現代瀏覽器一毛一樣。在IE10和IE11中使用Flexbox存在一些已知的問題,在caniuse-flexbox有說明,同時還有一個Flexbugs是一個問題的列表以及解決措施。

所以這裡的最佳實踐分兩步:

  1. 藉助Postcss外掛為我們自動加上字首,以支援IE10的2012版語法和現代語法;
  2. 使用過程避開在IE10和IE11中使用Flexbox的已知問題,如果還是碰到了在舊瀏覽器和現代瀏覽器中表現不一致,去Flexbugs 找找有沒有相同情況。如果再沒有,再考慮替換方案,也可以給 Flexbugs 這個專案提issue;

另外貼兩篇Postcss掃盲文章:Some things you may think about PostCSS... and you might be wrongIt's Time for Everyone to Learn About PostCSSWhat It Really Is; What It Really Does

Grid: Feature Queries

瀏覽器對Grid的支援較Flexbox要差很多,IE10、IE11支援的是舊版本的規範,是帶有-ms-字首,但即使使用autoprefixer補上了字首,相同屬性名相同屬性值在頁面中的表現也可能不一致。這樣我是不推薦Flexbox實踐中的方法,而是使用Feature Queries。

Feature Queries是使用css的@supports@supports用於檢測瀏覽器是否支援引數中的屬性屬性值,如果支援則渲染花括號中的css程式碼,類似於:

@supports (display: grid) {
   // code that will only run if CSS Grid is supported by the browser 
 }
複製程式碼

這裡有個細節點,IE10、IE11是不支援@supports規則,所以壓根不會進入這個條件判斷,花括號中的css程式碼是不會渲染的,這與我們考慮的邏輯:支援@supports規則、不支援display:grid是不同的,但是最後的結果是一樣的。

以一個例子講述一下整個流程:demo-creating fallbacks in CSS

  1. 首先是給舊瀏覽器做支援,準備一套Fallback method,保證在所有瀏覽器上都是工作的:
.wrapper{
  overflow:auto;
}

.item {
  float:left;
  width:33.3%;
}
複製程式碼
  1. 再給支援Grid的瀏覽器做覆蓋,覆蓋程式碼分兩部分,一部分是直接放入對舊瀏覽沒有影響的:
.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
}
複製程式碼

因為舊瀏覽器不支援Grid佈局,Grid相關屬性舊瀏覽器都無法解釋。在支援的瀏覽器中使得item由floated item轉為grid item,這樣的覆蓋行為由css規定,更多覆蓋情況見Fallback method。另一部分是直接放入對舊瀏覽器是有影響的,要做Feature queries:

@supports (display: grid) {
  .item {
      width: auto;
  }
}
複製程式碼

覆蓋原有的width:33.3%

沒錯,這裡的實踐得寫兩套樣式。所以有人提出問題,寫一套支援所有瀏覽器的不就得了,幹嘛非得用Grid?這是個很實際的問題,畢竟寫兩套,再加測試除錯,會增加一定工作量。有幾個場景建議使用Grid:

  1. 專案得支援IE10、IE11等舊瀏覽器,但是開發者想嚐鮮Grid佈局,Feature Queries提供了這樣的能力;
  2. 專案週期會很長,可能現在不支援Grid佈局的瀏覽器,以後就支援了;
  3. 要實現的效果不使用Grid佈局很難實現,且對在舊瀏覽器中訪問效果要求不高,能看就行;

測試

尤其是支援IE10、IE11的專案,測試是很重要的一個環節,最佳的測試還是在各個瀏覽器中開啟。但這裡存在獲取瀏覽器的問題,例如win10系統上僅有IE11,而不能使用IE10等。有些公司有自己的伺服器,有各種瀏覽器可供測試;如果沒有的話,可以考慮下載虛擬機器:download the Virtual Machines offered by Microsoft ,或者使用像 BrowserStack 訪問遠端的虛擬機器。

從開發者角度,整個工作流程應該是這樣子:

  1. 初始開發計劃制定
  2. 開發
  3. 測試、發現問題
  4. 修復問題,重複2~4步驟

總結

  1. 做css佈局
    1. 佈局的出發點是語義化標籤
    2. 考慮在具體場景下使用什麼佈局方法最合適最簡單
    3. 考慮要不要支援舊瀏覽器,要明確支援不意味著顯示一模一樣,可存在體驗優秀+體驗一般兩種模式
  2. Flexbox、Grid考慮舊瀏覽器的實踐(支援IE10、IE11)
    1. Flexbox支援性比Grid好,使用Autoprefixer字首,避開Flexbox bug、已知issues,放開了使用
    2. Grid佈局要想使用,得用Feature Queries的方法,額外準備一套Fallback Methods
    3. Autoprefixer關閉對Grid屬性新增字首(預設行為)
  3. 測試
    1. 測試流程:初始開發計劃制定 > 開發 > 測試、發現問題 > 修復問題,重複2~4步驟
    2. 藉助虛擬機器等

參考連結

MDN-CSS-layout

html-document-structured

Using Feature Queries in CSS

CSS Grid Layout and Progressive Enhancement

Using CSS Grid: Supporting Browsers Without Grid

Some things you may think about PostCSS... and you might be wrong

It's Time for Everyone to Learn About PostCSSWhat It Really Is; What It Really Does

相關文章