在眾多瀏覽器剛剛支援 CSS 的時候,我就已經開始使用它了,並且應該算是最早採用 CSS 進行頁面佈局的開發者之一了。那時候,瀏覽器之間的相容性雖然不好,但我仍然熱衷於嘗試層出不窮的新特性。最近幾年在 CSS 領域,我們看到了許多重大進展。其中,web 字型、漸變、陰影和媒體查詢已經成為了我們日常工作流的必備工具。
然而,CSS 佈局卻發展緩慢。開發者們曾經嘗試使用 display:table
和 display:inline-block
來佈局,用來緩解絕對定位和浮動佈局所帶來的束縛。然而,這些方法並不標準,因此它們又產生了新的問題。
CSS 佈局的未來看起來一片大好。在這篇文章中,我將會介紹 CSS 規範中一些激動人心的佈局模組。在未來,我們可以更有效地實現網格佈局,更輕鬆地建立等高列或者均勻分配內容到整個頁面。類似 Adobe 的公司往往熟悉佈局設計的細節,藉助它們的幫助來制定相關規範,我們就能更準確地控制頁面在瀏覽器上的顯示方式,同時避免對頁面內容的影響。
對於本文中示例,我已經在一個或多個瀏覽器上進行了測試,當然你也可以繼續測試它們。這些佈局模組中的一部分可能還處於發展的初期階段,其具體實現未來可能還會有變化,所以,你也可以就相關問題反饋給制定標準的團隊。這是我們的 web,我們應該熱衷於參與到制定規範的工作中。
如果你線上上產品中使用本書中的這些技巧,那麼你要確保頁面對低版本瀏覽器使用者同樣是友好的——即使這些低版本瀏覽器並不支援相關的佈局模組。雖然我不想花過多的時間解決瀏覽器的相容性問題,但在每一章節的最後,我會給你一些有用的建議和提示。
瀏覽器字首
本文所涉及到的大部分屬性,都會需要多個瀏覽器字首。對於那些規範比較穩定的佈局模組,比如多列布局和 flexbox 佈局,我會使用 Lea Verou 的 -prefix-free 指令碼,從而只需要演示標準屬性,也可以實現跨瀏覽器的相容性。對於線上產品,我建議你要麼為 CSS 新增瀏覽器字首,要麼使用 CSS 前處理器將瀏覽器字首編譯到最終的 CSS 檔案中。
對於那些非常新的佈局模組(只被個別瀏覽器所支援,相關規範頻繁變動的佈局模組),我會為其新增所測試瀏覽器的字首。在本書出版之時,通過新增特定瀏覽器字首,其他瀏覽器可能也已經支援相關佈局模組了。鑑於這些佈局模組的實驗性特徵,所以在不同瀏覽器上可能會有不同的渲染結果。應該儘可能地為其他瀏覽器使用字首並做相關的測試,最後線上上產品中還要加上無字首的相關屬性。
多列布局
CSS3 的多列布局已經風靡多年,然而,由於 IE 的不支援,它並沒有獲得預期的受歡迎程度。在 IE10 支援這些特性之後,它對於響應式設計就顯得更為有用了——非常期待它能流行起來。多列布局模組是本書提到的所有模組中,技術最成熟、瀏覽器支援度最高的一個模組,所以從它開始講述 CSS 中的新佈局模組是一個不錯的選擇。
多列布局使內容均勻分散到多列成為可能,它非常類似於報紙中的“內容流動”效果。首先,你需要在文件中選擇一個容器(container
),然後宣告該容器具有多列布局,那麼瀏覽器就能實現預期的多列效果。如果你為內容(content)指定了列數,瀏覽器就會自動計算出每一列的寬度,自動適應父級元素的尺寸;如果你指定了每一列的寬度,那麼瀏覽器就會自動計算列數,當父級元素尺寸發生變化,瀏覽器還可以自動重新計算。
示例:設定 column-width
–檢視示例
1 2 3 |
.col-wrapper { column-width: 220px; } |
設定 column-width
屬性意味著要求瀏覽器儘可能多地為容器建立縱列(columns),而開發者指定的寬度則會被視為理想寬度。你可能已經注意到了,當我們指定單列的寬度時,實際上並沒有得到預期的寬度。多列之間往往填充了空白(space),這有助於瀏覽器根據指定的寬度計算最合適的列數。CSS 多列規範中是這樣解釋的:
“
column-width
的值指定了最理想的單列寬度。實際的單列寬度可能會更寬一些(多列之間填充了空白),或更窄一些(只有可用寬度小於指定寬度時才會出現這種情況)。該屬性的指定值必須大於0
。”
因此,當需要設定單列寬度時,你只需指定一個理想寬度即可,因為為了設計的靈活性,實際寬度可能會出現一些差異。
示例:設定 column-count
–檢視示例
你也可以使用 column-count
指定所需要的列數,然後讓瀏覽器自行決定單列寬度。
1 2 3 |
.col-wrapper { column-count:3; } |
調節間距
在上面的例子中,你會發現多列之間並沒有緊緊相鄰:這是因為多列之間存在間距(gap)。在多列布局中,間距是由column-gap
屬性控制的。如果將 column-gap
設為 0
,那麼多列之間就不會有間距,所有的文字會擁擠在一起。規範中建議瀏覽器在預設樣式中為該屬性預設 1em
的寬度。不過,如果讓多列的間距保持一致性,那麼你應該顯式地為該屬性設定一個值。
示例:設定 column-gap
屬性–檢視示例
1 2 3 4 |
.col-wrapper { column-width: 220px; column-gap: 1.5em; } |
美化多列布局
在最新的規範中已經限制了對多列布局的美化。不過,相關的工作草案也提示說:“或許在未來的規範中會新增額外的功能。比如,允許多列中的任意一列設定不同的寬度和背景。”目前來說,你還不能單獨地美化多列中的某一列。
雖然沒法設定單列的內外邊距、寬度和背景色,但是我們可以使用一些規則來隔離多列。實現這一佈局效果需要使用column-rule
屬性:
column-rule-style
column-rule-width
column-rule-color
這些屬性的用法非常類似 border-style
、border-width
和 border-color
,而且也可以使用 column-rule
屬性作為一種簡寫形式:
1 |
column-rule: [width] [style] [color]; |
上面的縱列屬性(column rules)將會被應用到 column-gap
上。要想修改縱列兩邊的空白間距,就需要調整column-gap
的屬性值。如果上述縱列屬性的值大於可用間距,那麼它就會與文字區重疊——但它不會佔用任何的空間。
示例:使用 column-rule
–檢視示例
單列跨度(span)
如果你想讓某一個元素延伸到所有的縱列中,那麼可以為該元素新增 column-span
並賦值為 all
。在下面的示例中,我要讓 h1
標題可以延伸到所有的縱列上。
示例:強制 h1
延伸到多列–檢視示例
1 2 3 4 |
.col-wrapper h1 { column-span: all; padding: 0 0 .5em 0; } |
當前的規範中 column-span
只有兩個值:all
和 none
。
多列截斷
當使用多列布局的時候,你需要控制多列截斷的方式。如果不希望某些元素被截斷到新的縱列,或者確保某個元素固定在某一列時,下面的這個屬性對你就會很有幫助。
示例:避免在段落內和引用塊之前被截斷–檢視示例
1 2 3 4 5 6 |
.col-wrapper p { break-inside: avoid; } .col-wrapper blockquote { break-before: avoid; } |
印刷和分頁媒體型別
規範指出,多列布局內部的元素不應該出現在下一頁上。如果閱讀時翻到了下一頁,然後讓使用者再返回前一頁繼續閱讀,那麼這樣的體驗就太讓人惱火了。
你可以預設分頁時段落或者元素內部內容的佈局方式——就像控制多列截斷一樣。屬性 avoid-page
和 avoid-column
就可以幫助你實現良好的控制。如果你允許段落進行截斷而不允許內容分頁,那麼對於上面的例子,就可以使用 break-inside: avoid-page
替代 break-inside: avoid;
。
響應式設計
由於多列預設就是響應式的,所以多列布局也有助於實現響應式設計。正如我們所知,雖然可以根據需求設定單列寬度,但本質上瀏覽器會使用自己的演算法,計算出一個用於渲染的寬度。
單列內的圖片會被限制在單列範圍之內,所以使用 max-width
設定的最大寬度也被侷限在單列之內。如果你沒有為圖片設定 max-width: 100%;
,同時圖片的寬度還大於單列寬度,那麼瀏覽器就會自動裁剪掉多餘的部分。該規則同樣適用於其他寬度大於單列寬度的元素。
示例:圖片寬度限制於單列之內–檢視示例
一定不要認為多列布局模組只適用於建立類似報紙的版式。下面示例中的多列布局,就包含了一系列的盒模型和圖片。
示例:盒模型和圖片–檢視示例
下面的示例演示瀏覽器視口較窄時的單列布局,無需編寫額外的程式碼即可實現。
瀏覽器提示
當我寫下這些文字的時候,還只有幾個瀏覽器支援多列布局。因此,有時你需要新增瀏覽器字首才能使用這些屬性,此外,在有一些情況下某些屬性並不會生效。更多更新的瀏覽器支援資訊,可以檢視 Can I Use。如果瀏覽器不支援多列布局,那麼它在解析樣式表時就會忽略相關屬性,因此可以放心使用這些屬性。不支援多列布局的瀏覽器會將內容渲染為單列,這種渲染結果在大多數情況下也是可以接受的——不建議使用膩子指令碼模擬多列布局效果。
CSS Flexbox佈局
CSS 彈性盒佈局模組,通常被稱為 flexbox,為我們提供了一種新的佈局——flex 佈局。設計該佈局的初衷是為了簡化複雜應用和頁面的佈局程式碼。在本節中,我將會著重介紹一些使用 flexbox 解決的佈局問題。
均勻排列
在傳統的佈局設計中,將一組佈局元素沿座標軸均勻排列是件很麻煩的工作。如果使用浮動佈局,那麼每個浮動元素都必須設定一個寬度,否則就會寬窄不一,此外,往往需要使用 JavaScript,才能讓所有的浮動元素均勻排列在同一行上。
Flexbox 極大簡化了這一佈局過程。在下面的示例中,我使用無序列表建立了一個導航條。
示例:flexbox 簡單用法–檢視示例
1 2 3 4 5 6 7 8 9 |
<nav class="mainnav"> <ul> <li><a href="">Introductory</a></li> <li><a href="">The First Cat Show</a></li> <li><a href="">Habits</a></li>
<li><a href="">Trained Cats</a></li> <li><a href="">Usefulness of Cats</a></li> </ul> </nav> |
對於這個導航條,我想實現水平均勻分佈的效果。如果我們選擇 flexbox 佈局方式,那麼只需要新增 display:flex
屬性,並指定佈局元素的排列方式(所有元素平均排列或者除首尾元素外平均排列)。
示例:flexbox 簡單用法–檢視示例
1 2 3 |
nav ul{ margin: 0;
padding: 0;
list-style: none;
display: flex;
justify-content: space-between; } |
在這裡,我們將 justify-content
設定成了 space-around
,這樣做的好處就是讓每一個元素之間具有相同的間距,避免了溢位容器的問題。此外,在第一個元素之前和最後一個元素之後,也新增了相同的間距。
上面的示例使用了一些 flexbox 的預設屬性,比如這裡的佈局元素預設顯示為水平排列。這個預設的排列效果等同於新增了 flex-direction: row
的效果。
flex-direction
屬性一共擁有四個屬性值:row
、row-reverse
、column
和 column-reverse
。使用這些屬性,可以實現水平排列、反向水平排列、垂直排列和反向垂直排列四種佈局效果。
1 2 3 4 5 6 7 8 |
nav ul { margin: 0; padding: 0; list-style: none; display: flex; justify-content: space-between; flex-direction: row-reverse; } |
使用 flexbox 的另一個優勢在於,它可以幫你建立等高容器——即使容器內的文字不等長。align-items
的預設屬性值為 stretch
,它會根據 flexbox 內最高元素的高度,拉伸其他佈局元素,使之等高。在我的導航示例中,如果你縮窄視窗,文字就會自適應為多行,但仍然保持等高拉伸的佈局——從所有元素的邊框就可以看出他們是等高的。
align-items
的所有屬性值:
flex-start
flex-end
center
baseline
stretch
為了理解其正確的解析方式,你需要首先理解 flexbox 中的兩個軸概念:主軸和側軸,其中主軸用來控制元素的佈局方向。通過將 flex-direction
設定為 row
或者 column
,可以指定主軸,並確定佈局元素的排列方向:從左到右或者從上到下;第二條軸,即側軸,垂直於主軸。
如果將 flex-direction
設為 row
,那麼主軸方向就是從左到右的。設定 align-items
為 flex-end
,意味著佈局元素不會佔據佈局容器的全部高度,所有的佈局元素在底部對齊,不做等高處理。
多行 flexbox
在之前的這個示例中,如果我們將瀏覽器縮窄,那麼佈局元素內的文字就會在佈局元素內拆分成多行。究其原因,是因為佈局元素的內容寬度超過了可用空間。一種可行的解決方案就是將佈局容器設為 wrap
(表現為多行效果)。
在 flexbox 佈局中,使用 flex-wrap
屬性可以控制佈局容器的多行模式,可用的屬性值包括 wrap
、nowrap
和wrap-reverse
。如果沒有顯式宣告該屬性值,那麼 flex-box
預設為 nowrap
。
示例:佈局容器的多行模式–檢視示例
1 2 3 4 5 6 7 8 |
nav ul{ margin: 0; padding: 0; list-style: none; display: flex; justify-content: space-between; align-items: stretch; flex-direction: row-reverse; flex-wrap: wrap; } |
然而,從圖中可以發現,相鄰佈局元素之間出現了意料之外的不規則空隙。修正這種表現的方式是為佈局元素新增額外的佈局間距。為佈局元素設定 flex: auto;
即可實現這一目標——在我們的示例中,我為 li
元素新增了該屬性。
1 2 3 4 5 6 7 |
nav li { border: 1px solid #999; border-radius: 2px; flex:auto; margin: 0 1em 1em 0; text-align: center; } |
使用 flex
屬性
雖然為佈局元素設定 flex: auto;
可以調整它們在佈局容器內的空間排列,但該屬性的作用遠不止如此,這裡將會介紹幾種其他的用法。
在下面的示例中,我編寫了三個塊級元素,塊級元素內部是一些關於長毛貓品種的資訊。此外,我還為塊級元素新增了公有的類名 box
和獨立的類名,便於選擇特定的塊級元素。
示例:三個塊級元素–檢視示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div class="boxes"> <div class="box box1"> <h2>The Angora</h2> <p>... </p> </div> <div class="box box2"> <h2>The Persian Cat</h2> <p>... </p> </div> <div class="box box3"> <h2>The Russian Long-haired Cat</h2> <p>... </p> </div> </div> |
為了讓所有的塊級元素在單行內排列,我將佈局容器,即 boxes
,設為了 flex 元素,然後為容器內的每個元素設定flex: 1
。設定完成後,所有的佈局元素就都具有了相同的寬度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.boxes { display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: stretch; justify-content: space-between; } .box { border: 1px solid #999; border-radius: 5px; flex: 1;
margin: 0 1em 1em 1em; padding: 10px; } |
此外,我們可能需要讓某個佈局元素寬於其他佈局元素,同時還要根據可用的容器空間計算各個佈局元素的寬度。示例中的第三個塊級元素擁有一個類名 box3
,如果為 box3
新增 flex
屬性並賦值為 2
,那麼相比於 flex: 1;
的佈局元素,它就會具有兩倍的寬度。
1 2 3 |
.box3 { flex: 2; } |
為 flex 佈局元素排序
從前面的介紹中,我們已經瞭解到 flex 佈局元素是可以反向排列的。實際上,我們可以為每個獨立的佈局元素設定任意的排列順序,其中的關鍵,就需要用到下面介紹的 order
屬性。
通過為每個佈局元素設定 order
屬性,我可以輕鬆移動 box3
。只需要為其新增 order: 2;
,就可以將這個最寬的佈局元素移動到容器中央。
1 2 3 4 5 6 7 8 9 10 |
.box1 { order: 1; } .box2 { order: 3; } .box3 { flex: 2; order: 2; } |
雖然我們更改了渲染後的佈局順序,但實際上它的 HTML 結構仍舊保持不變。這意味著你可以根據實際需求來編寫 HTML 結構,這樣做有助於提高可用性,改善使用文字閱讀器的使用者體驗。此外,使用該屬性還可以建立出色的佈局效果。
響應式設計
對於響應式設計,flexbox 是一種優秀的可選方案。由於它具有包裹多行、自適應可用空間的特點,可以讓我們不費吹灰之力就建立出簡單的響應式效果。
如果你在 flexbox 中混合媒體查詢的功能,那麼就可以建立出更復雜的佈局效果。由於可以按照不同於原始碼的結構來顯示佈局元素,所以我們能夠在不同尺寸的螢幕上建立不同的佈局效果。此外,也可以像下面的示例一樣,使用 flex-direction
來轉換佈局方向。最初,我們可以讓導航顯示為垂直排列,當視窗寬度大於 700px
時,我們就可以將其轉換為水平排列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
nav ul{ margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; justify-content: space-between; align-items: stretch; } @media only screen and (min-width: 700px) { nav ul { flex-direction: row; } } |
瀏覽器提示
Flexbox 是一個極佳的案例,展示了 CSS 規範在初期的演變方式。由於 flexbox 的具體實現和最初構想之間已經發生了諸多變動,所以在 flexbox 的可用性上存在大量無效的資料。當你需要檢索有關 flexbox 的資料時,可以檢視一下 CSS Tricks 上的相關文章,便於檢驗當前資料的準確性。
當瀏覽器不支援 flexbox 時,一方面可以使用 JavaScript 來模擬 flexbox 的大量特性,另一方面你也可以讓瀏覽器優雅降級,讓佈局元素呈現一種線性排列,下圖就是之前的佈局元素在瀏覽器不支援 flexbox 時的渲染效果。
如果你只是將 flexbox 佈局模組應用於少量的介面元素,而不是整體佈局,那麼優雅降級為線性排列是比較簡單的。另一種方式是使用 Modernizr 檢測瀏覽器是否支援 flexbox,然後分別為支援和不支援 flexbox 的情況編寫不同的 CSS 程式碼。
CSS 網格佈局
CSS 網格佈局模組是由微軟提議的,目前仍在進行大量的規範化工作。該模組的最新進展已經應用到了 Internet Explorer 10 上,所以本部分的例項演示都會基於 IE10。
網格佈局旨在解決複雜網頁的佈局問題——在該佈局提出之前,我們只能通過元素的浮動和定位來模擬複雜佈局。網格佈局也允許開發者顯示與原始碼結構不同的佈局結構——類似上一章節的 flexbox 佈局。我很喜歡網格佈局,希望你在閱讀完下面的示例之後也會喜歡它。
建立網格
將佈局元素包裹進網格的第一步,就是在它們的父元素上建立網格。首先需要新增 display: grid;
(通常我會加上-ms-
字首),然後設定所需的網格行數和列數。
示例:一個簡單的網格佈局–檢視示例
1 2 3 4 5 |
.wrapper { display: -ms-grid; -ms-grid-columns: 200px 20px auto 20px 250px; -ms-grid-rows: auto 1fr; } |
上面的 CSS 程式碼在 .class
元素上建立了網格,該網格擁有五個縱列:一個 200px
寬度的側列,一個 20px
寬度的間距,一個自適應寬度的中間縱列,另一個 20px
寬度的間距,以及最後一個 250px
寬度的縱列。由此可見,這是由兩個固定寬度的縱列和一個可變寬度的中間縱列構成的簡單佈局。
在橫列上,我將第一列設為了 auto
——它將根據內部內容自動擴充套件高度,將第二列設為了 1fr
。1fr
準確的說是一個分數比值,在這裡意味著第二列在剩餘空間所佔的比重。由此可見,這是具有兩個橫列的佈局。
現在,我們可以往網格里新增一些內容了。新增內容後的結構如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<div class="wrapper"> <nav class="mainnav"> <ul> <li><a href="">Introductory</a></li> <li><a href="">The First Cat Show</a></li> <li><a href="">Habits</a></li> <li><a href="">Trained Cats</a></li> <li><a href="">Usefulness of Cats</a></li> </ul> </nav> <h2 class="subhead">Usefulness of cats</h2> <article class="content"> <p>...</p> </article> <blockquote class="quote"> <p>....</p> </blockquote> </div> |
為了將導航定位到左側固定寬度的縱列上,將文章部分定位到中間的縱列上,將引用定位到右邊的縱列上,我又新增了如下的樣式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
.mainnav { -ms-grid-column: 1; -ms-grid-row: 2; } .subhead { -ms-grid-row: 1; -ms-grid-column:3; } .content { -ms-grid-column: 3; -ms-grid-row: 2; } .quote { -ms-grid-column: 5; -ms-grid-row: 2; } |
上面所做的處理就是在指定具體的內容所對應的縱列。一定要牢記,間距也是縱列,所以主內容區的是在第三列上,最右側的縱列是第五列。
當我嘗試理解這種網格佈局時,我發現將其設想為傳統的表格佈局會很有幫助,不同之處在於網格佈局是在 CSS 中實現的。然後,我要做的是將佈局元素放入表格單元,不過,不同於表格佈局,為了適配不同尺寸的顯示螢幕,在 CSS 中重新定義網格結構是更加方便的。這意味著在將來,網格佈局會成為響應式設計強有力的工具。
網格和響應式設計
現在,我要在這裡涉及一些複雜的佈局——這些佈局更具有實用性,展示網格佈局在響應式設計中的實際應用。
示例中使用的文件結構很簡單:一個新增了 wrapper
類名的 div
標籤;五個新增了 box
類名的 div
標籤,標籤內新增了一些內容;以及一張關於貓的圖片標籤——圖片來自維多利亞時代的書籍。為了簡單起見,我給每個 box
元素都新增了第二個類名,便於在 CSS 中對其進行定位和檢索。
示例:一個響應式的網格佈局–檢視示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<h1 class="title">Extracts from "Our Cats, by Harrison Weir"</h1> <div class="wrapper"> <div class="box content1"> <h2>The first cat show</h2> <p> ...</p> </div> <div class="box content2"> <h3>The Angora</h3> <p>... </p> </div> <div class="box content3"> <h3>The Persian Cat</h3> <p>... </p> </div> <div class="box content4"> <h3>The Russian Long-haired Cat</h3> <p>...</p> </div> <div class="box picturebox"> <figure> <img src="fluffy.jpg" alt="Fluffy the cat" /> <figcaption>Fluffy, the cat</figcaption> </figure> </div> </div> |
只需要使用少量基礎的 CSS 屬性做一下美化,我們就得到了一個如下圖所示的線性設計(linearised design)。
下面,需要定義網格了。我是通過給 .wrapper
新增相關的屬性實現的:
1 2 3 4 5 6 7 |
.wrapper { width: 90%; margin: 0 auto 0 auto; display: -ms-grid; -ms-grid-columns: 1fr (4.25fr 1fr)[6]; -ms-grid-rows: (auto 20px)[4]; } |
通過設定 display: -ms-grid;
,表示了該元素內部包含一個網格佈局。通過使用一個簡寫形式的多列語法,我建立了多個縱列。將有關縱列的引數集合放入圓括號內,其後跟一個引數為數字的中括號,這種語法表示我們想要重複設定某種縱列型別:在這個示例中,這意味著我將這兩個縱列重複了六次。縱列之間的間距被設定為 1
個分數單位,而縱列則擁有 4.75
個分數單位的寬度。
然後,我使用相同的語法設定了橫列,建立了一個根據內容自適應高度的橫列,其後跟著一個 20px
寬度的間隙,最後將上述的橫列格式重複了四次。
如果新增完上述的 CSS 後重新整理頁面,你會發現所有的縱列都被摺疊了。這是因為所有的佈局元素都嘗試去填充第一縱列和第一橫列,我們需要進一步為佈局元素設定定位資訊。一旦你為某個元素宣告瞭網格資訊,那麼所有的子元素就都需要在網格中進行定位。
為了適配小尺寸的螢幕,我將從元素定位開始做佈局。由於使用者使用小尺寸裝置時是從上到下閱讀的,所以我會按照原始碼中的內容順序來佈局,而對於使用螢幕閱讀器的使用者,我會根據內容的重要性來安排佈局順序。在分離原始碼結構和佈局結構這個新領域,我無需過多擔心視覺效果背後的原始碼結構。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
.content1 { -ms-grid-row: 1; -ms-grid-column: 2; -ms-grid-column-span:12; } .content2 { -ms-grid-row:3; -ms-grid-column: 2; -ms-grid-column-span:5; } .content3 { -ms-grid-row:3; -ms-grid-column:8; -ms-grid-column-span:6; } .content4 { -ms-grid-row:7; -ms-grid-column: 2; -ms-grid-column-span:12; } .picturebox { -ms-grid-row:5; -ms-grid-column: 2; -ms-grid-column-span:12; } |
這些屬性看起來都相當簡單。現在,我需要選擇具體的橫列放入合適的內容。一定要牢記橫列中間有 20px
的間距,所以我們應該將佈局元素放入奇數橫列中。然後,我為每個區域設定了起始縱列和縱列跨度。到此為止,大多數佈局元素的寬度都是等於容器寬度的,但我將其中的 content2
和 content3
分割成了兩個縱列。現在,在瀏覽器中就可以顯示出一個簡單的佈局了。
接著,我要新增第一個媒體查詢了,讓佈局在瀏覽器視窗大於 700px
的寬度時發生一些改變。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
@media only screen and (min-width: 700px) { .wrapper { -ms-grid-columns: 1fr (4.25fr 1fr)[9]; -ms-grid-rows: (auto 20px)[5]; } .content1 { -ms-grid-row: 1; -ms-grid-column: 2; -ms-grid-column-span:17; } .content2 { -ms-grid-row:3; -ms-grid-column:8; -ms-grid-column-span:5; } .content3 { -ms-grid-row:3; -ms-grid-column:14; -ms-grid-column-span:5; } .content4 { -ms-grid-row:3; -ms-grid-column: 2; -ms-grid-row-span: 3; -ms-grid-column-span:5; } .picturebox { -ms-grid-row:5; -ms-grid-column: 8; -ms-grid-column-span:11; } } |
對於較寬的瀏覽器視窗,我重新界定了網格,增加了更多的縱列和一個額外的橫列。佈局頂部的第一個內容區的寬度仍然佔滿了整個容器。不過,我將其他的內容去分隔成了三個縱列,其中一個內容區跨越兩個橫列,另外兩個內容區只有前者高度的一半,並將圖片放在了這兩個內容區的下面。
最後,我使用媒體查詢,為 940px
以及更寬的螢幕重新設計了網格佈局。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@media only screen and (min-width: 940px) { .wrapper { -ms-grid-columns: 1fr (4.25fr 1fr)[16]; -ms-grid-rows: (auto 20px)[3]; } .content1 { -ms-grid-row:1; -ms-grid-row-span: 3; -ms-grid-column: 20; -ms-grid-column-span:13; } .content4, .content2, .content3 { -ms-grid-row:1; } .picturebox { -ms-grid-row:3; } } |
這次重新界定的網格是一個基於 960 網格系統的標準網格佈局。現在,我建立的網格佈局中,包含了三個窄小的縱列和一個較寬的縱列,圖片被放在了較窄的兩個縱列下面。
網格佈局模組真正的魅力在於,我們無需改變原始碼中的標記結構,即可輕鬆變換內容在佈局中的位置。我認為該模組對於初次接觸它的開發者也是通俗易懂的,尤其是對於那些和我一樣還記得表格佈局的老派開發者。
瀏覽器提示
正如本章開始所解釋的,因為網格佈局語法是由微軟提出的,所以該模組目前只被 IE10 所支援。如果你正在開發 Windows 方面的應用程式,那麼可能已經在相關的開發環境中用到它了。我也希望看到它被更多的瀏覽器所支援。
CSS Regions
本文的最後兩章將會討論一些由 Adobe 和微軟提議的新方案,它們都很有意思。雖然這些新方案的發展比起網格佈局來說還處於初級階段,但它們可以建立一些不可思議的佈局效果。此外,從探究 CSS 佈局中的新特性這個角度來說,也是非常值得的。
CSS Regions 語法為內容流建立了一種高階模型。我們可以通過指定文件的特定區域,讓該區域的內容實現“流動顯示”的效果——即使指定的這些區域在文件結構中並不相鄰。在 web 環境中,這聽起來有些怪異,不過,這種佈局常常被用於印刷設計,特別是雜誌和報紙。
CSS Regions 並沒有提出任何新的佈局方法,所以可以使用現有的或將來會出現的方法來定位元素。
為了演示 CSS Regions 的使用方式,最簡單的方法就是舉例說明。在編寫本書的時候,只有 Chrome Canary 版本的瀏覽器支援該佈局,而且還要保證已經開啟了 WebKit 的實驗性特性。如果你有這樣的瀏覽器,那麼就可以把玩下面的示例程式碼了。
下面就是示例的 HTML 結構。我使用了一個 article
標籤來包裹文章的所有內容,並在文件底部新增了多個空標籤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div class="wrapper"> <article class="main"> <h2>Usefulness of cats</h2> <p>...</p> <p>...</p> </article> <div class="region1 article-regions"></div> <div class="regionwrapper"> <div class="region2 article-regions"></div> <div class="region3 article-regions"></div> <div class="region4 article-regions"></div> </div> <div class="region5 article-regions"></div> </div> </div> |
article
標籤內包含了佈局前的原始內容,而後面的空標籤則充當了這些內容的容器。
首先,我需要為原始內容的容器新增一個新的 CSS 屬性 flow-into
(附加 -webkit
字首),該屬性的值article-thread
是我為原始內容宣告的一個名字,也可以用任何其他的名字。
1 2 3 |
.main { -webkit-flow-into: article-thread; } |
一旦新增了該屬性,那麼原始內容就會消失,這是因為你還沒有指定它們的佈局位置。
在上面的 HTML 結構中,每個空標籤都擁有一個 article-regions
的類名,所以我可以讓它們接收原始內容。
1 2 3 |
.article-regions { -webkit-flow-from: article-thread; } |
在這裡,我們使用了一個新的屬性 flow-from
,並匹配了之前為原始內容宣告的名字。如果現在重新整理頁面,就會發現內容重新出現在頁面上了。接下來,就可以讓第一個區域“流動起來了”。當內容“流動起來”之後,我們新增 CSS 來指定流動區域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
.region1 { height: 10em; } .regionwrapper { display: flex; flex-direction: row; } .region2 { flex: 1; padding: 10px; height: 40em; border-right: 1px dotted #ccc; } .region3 { flex: 1; padding: 10px; height: 40em; border-right: 1px dotted #ccc; background-color: #efefef; } .region4 { flex: 1; padding: 10px; height: 40em; } .region5 { padding: 1em 0 0 0; margin: 1em 0 0 0; border-top: 1px dotted #ccc; height: auto; } |
現在 region1
區域已經擁有了 10em
的高度。一旦內容超過了這個高度,內容就會嘗試填充到其他地方,所以在這裡它會流入下一個區域。接下來的三個區域通過使用 flexbox 佈局,被定位到了三個彈性縱列中,所以內容可以“流”入這些區域。由於這三個區域是固定高度的,當內容充滿它們之後,就會繼續“流”入最後的區域,該區域具有自適應的高度,可以容納剩餘的所有內容。
如果我改變想法了,想要在內容中間插入其他的東西,那麼我也是可以做到的。在下面的示例中,我從 flexbox 中間的縱列上移除了 artcle-regions
類名,所以內容就不會再“流”入其中。現在我可以為這個縱列新增一張有關貓的圖片,並引用一句話——它們和周圍“流動的內容”並沒有多大關係。
瀏覽器提示
在本章,我們嘗試了 CSS 模組中非常新穎的部分,這些部分目前只在 WebKit 瀏覽器開啟實驗性特性時被支援。雖然這並不是一個可以用於線上環境的技巧,但我認為 CSS Regions 所創造的無限可能是一件激動人心的事。
CSS Exclusions
如果你曾經需要用到文字環繞圖形的效果,或者想要裁剪位於內容中央的圖形,那麼你可能會對 CSS Exclusions 和 Shapes 模組感興趣。一個早期的、參考了浮動定位的規範,已經被當前的工作草案所取代。
我們都非常熟悉 CSS 中的浮動,關於浮動最簡單的例子就是浮動圖片被文字環繞的效果。不過,浮動在這種場景下是受到嚴格限制的。浮動元素總是出現在頂部,當我們將圖片浮動到左側時,文字就會環繞在它的右側和下方,無法將圖片置於文件的中央,實現文字的四周環繞效果,也無法將其浮動到底部,讓文字環繞在上方和兩側。上面提到的,就是 Exclusions 和 Shapes 模組首要解決的問題,並已經在 IE10 和開啟實驗性特性的 Webkit 瀏覽器中獲得了支援,下面我們將會看一個例項。
這個例項就是一段簡單的文字,其外層被一個類名為 main
的 article
標籤所包裹。在 article
標籤之後,緊跟著一個 div
標籤,標籤內部是一張貓的圖片。我希望這張圖片在佈局時呈現一種文字環繞的效果。
1 2 3 4 5 6 7 8 9 |
<div class="wrapper"> <article class="main"> <h2>Usefulness of cats</h2> <p>...</p> </article> <div class="exclusion"> <img src="fluffy.jpg" alt="Fluffy the cat" /> </div> </div> |
首先,我要像之前一樣為它建立一個網格。與浮動不同的是,Exclusion 需要被定位(因此在浮動定位前需要有個名字)。我是使用 IE10 演示該示例的,所以可以使用網格佈局來實現這種定位。
我建立的是一個兩行三列的網格,並在建立後讓 article
佈局範圍覆蓋到了所有的行列上。雖然 div
標籤在原始碼結構中是緊跟在 article
標籤之後的,但它會顯示在文字的上方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.main { -ms-grid-column: 1; -ms-grid-row: 1; -ms-grid-row-span: 3; -ms-grid-column-span: 3; padding: 0 0 2em 0; } .exclusion { -ms-grid-row:2; -ms-grid-column:2; height: 160px; width: 200px; padding: 10px; } |
要建立 Exclusion,實現浮動圖片的文字環繞效果,我只需要為 .exclusion
新增 wrap-flow
屬性即可。
1 |
.exclusion { -ms-grid-row:2; -ms-grid-column:2; height: 160px; width: 200px; padding: 10px; -ms-wrap-flow:both; } |
屬性值 both
意味著文字將會環繞在 Exclusion 的兩側。
實現矩形的文字環繞只是 CSS Exclusions 和 Shapes 模組的一小部分特性。在一個早期的瀏覽器實現中,既可以讓文字環繞圖形的外部,也可以將文字約束在圖形內部。如果你可以使用 Chrome Canary,並且開啟了 WebKit 的實驗性特性,那麼就可以檢視由 Adobe 提供的更多示例,甚至是建立自己的例項。這些特性還算是新穎的東西,並具有一定的複雜性,雖然已經在 WebKit 上做了很多測試,依然有可能在最新的瀏覽器上發生變化。
瀏覽器提示
wrap-flow
特性對於控制文字環繞效果非常有用,它一旦被大多數的瀏覽器所支援,一定會快速流行起來。合理構建文件的原始碼結構,有助於在不支援上述特性的瀏覽器中優雅降級到另一種佈局方案;也有助於在使用類似 Modernizr 的 JavaScript 特性檢測方式之後,提供一種可替換的定位模型。
甚至是 Shapes,一旦它被主流瀏覽器所支援,那麼就可以考慮去使用它了。只要文字環繞對於你的設計是一種有益的、非必須的特性,毫無疑問你就可以為那些支援這些特性的瀏覽器新增它,達到畫龍點睛的效果——特別是當你知道訪問網站的使用者常常會使用這些瀏覽器的時候。
總結
推敲、把玩和測試這些佈局模組是一件激動人心而又妙趣橫生的事情。我非常希望本文的內容能夠點燃你的熱情,讓你繼續去探索 CSS 中既有和即將到來的新特性,去發現無限的可能性。
在學習這些出色的新特性時,雖然它們可能還不能用於線上實踐,雖然會有種種障礙,但對於我們自身的職業發展將會有莫大幫助。參與這些新規範的建立和測試,並不是瀏覽器開發者和大公司的專利。如果希望未來在瀏覽器中用到這些新規範,作為網頁設計師和開發者的我們,就應該參入到其中。更多資訊可以檢視推動 Web 發展這個網站:這裡提供了多種方式讓你參與到 Web 標準的制定中。
本文根據@Rachel Andrew的《CSS3 Layout Modules》電子書所整理。如果對此內容感興趣,可以購買電子書閱讀:http://www.fivesimplesteps.com/products/css3-layout-modules。