CSS 布局模塊
原文出處: w3cplus - 南北
在眾多瀏覽器剛剛支持 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-查看示例
.col-wrapper { column-width: 220px; }
設置column-width屬性意味著要求瀏覽器盡可能多地為容器創建縱列(columns),而開發者指定的寬度則會被視為理想寬度。你可能已經注意到了,當我們指定單列的寬度時,實際上并沒有得到預期的寬度。多列之間往往填充了空白(space),這有助于瀏覽器根據指定的寬度計算最合適的列數。CSS 多列規范中是這樣解釋的:
“column-width的值指定了最理想的單列寬度。實際的單列寬度可能會更寬一些(多列之間填充了空白),或更窄一些(只有可用寬度小于指定寬度時才會出現這種情況)。該屬性的指定值必須大于0。”
因此,當需要設定單列寬度時,你只需指定一個理想寬度即可,因為為了設計的靈活性,實際寬度可能會出現一些差異。
示例:設置column-count-查看示例
你也可以使用column-count指定所需要的列數,然后讓瀏覽器自行決定單列寬度。
.col-wrapper { column-count:3; }
調節間距
在上面的例子中,你會發現多列之間并沒有緊緊相鄰:這是因為多列之間存在間距(gap)。在多列布局中,間距是由column-gap屬性控制的。如果將column-gap設為0,那么多列之間就不會有間距,所有的文字會擁擠在一起。規范中建議瀏覽器在默認樣式中為該屬性預設1em的寬度。不過,如果讓多列的間距保持一致性,那么你應該顯式地為該屬性設定一個值。
示例:設置column-gap屬性-查看示例
.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屬性作為一種簡寫形式:
column-rule: [width] [style] [color];
上面的縱列屬性(column rules)將會被應用到column-gap上。要想修改縱列兩邊的空白間距,就需要調整column-gap的屬性值。如果上述縱列屬性的值大于可用間距,那么它就會與文本區重疊——但它不會占用任何的空間。
示例:使用column-rule-查看示例
單列跨度(span)
如果你想讓某一個元素延伸到所有的縱列中,那么可以為該元素添加column-span并賦值為all。在下面的示例中,我要讓h1標題可以延伸到所有的縱列上。
示例:強制h1延伸到多列-查看示例
.col-wrapper h1 { column-span: all; padding: 0 0 .5em 0; }
當前的規范中column-span只有兩個值:all和none。
多列截斷
當使用多列布局的時候,你需要控制多列截斷的方式。如果不希望某些元素被截斷到新的縱列,或者確保某個元素固定在某一列時,下面的這個屬性對你就會很有幫助。
示例:避免在段落內和引用塊之前被截斷-查看示例
.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 簡單用法-查看示例
<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 簡單用法-查看示例
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。使用這些屬性,可以實現水平排列、反向水平排列、垂直排列和反向垂直排列四種布局效果。
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。
示例:布局容器的多行模式-查看示例
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元素添加了該屬性。
nav li { border: 1px solid #999; border-radius: 2px; flex:auto; margin: 0 1em 1em 0; text-align: center; }
使用flex屬性
雖然為布局元素設置flex: auto;可以調整它們在布局容器內的空間排列,但該屬性的作用遠不止如此,這里將會介紹幾種其他的用法。
在下面的示例中,我編寫了三個塊級元素,塊級元素內部是一些關于長毛貓品種的信息。此外,我還為塊級元素添加了公有的類名box和獨立的類名,便于選擇特定的塊級元素。
示例:三個塊級元素-查看示例
<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。設置完成后,所有的布局元素就都具有了相同的寬度。
.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;的布局元素,它就會具有兩倍的寬度。
.box3 { flex: 2; }
為 flex 布局元素排序
從前面的介紹中,我們已經了解到 flex 布局元素是可以反向排列的。實際上,我們可以為每個獨立的布局元素設置任意的排列順序,其中的關鍵,就需要用到下面介紹的order屬性。
通過為每個布局元素設置order屬性,我可以輕松移動box3。只需要為其添加order: 2;,就可以將這個最寬的布局元素移動到容器中央。
.box1 { order: 1; } .box2 { order: 3; } .box3 { flex: 2; order: 2; }
雖然我們更改了渲染后的布局順序,但實際上它的 HTML 結構仍舊保持不變。這意味著你可以根據實際需求來編寫 HTML 結構,這樣做有助于提高可用性,改善使用文本閱讀器的用戶體驗。此外,使用該屬性還可以創建出色的布局效果。
響應式設計
對于響應式設計,flexbox 是一種優秀的可選方案。由于它具有包裹多行、自適應可用空間的特點,可以讓我們不費吹灰之力就創建出簡單的響應式效果。
如果你在 flexbox 中混合媒體查詢的功能,那么就可以創建出更復雜的布局效果。由于可以按照不同于源碼的結構來顯示布局元素,所以我們能夠在不同尺寸的屏幕上創建不同的布局效果。此外,也可以像下面的示例一樣,使用flex-direction來轉換布局方向。最初,我們可以讓導航顯示為垂直排列,當窗口寬度大于700px時,我們就可以將其轉換為水平排列。
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-前綴),然后設置所需的網格行數和列數。
示例:一個簡單的網格布局-查看示例
.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準確的說是一個分數比值,在這里意味著第二列在剩余空間所占的比重。由此可見,這是具有兩個橫列的布局。
現在,我們可以往網格里添加一些內容了。添加內容后的結構如下:
<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>
為了將導航定位到左側固定寬度的縱列上,將文章部分定位到中間的縱列上,將引用定位到右邊的縱列上,我又添加了如下的樣式:
.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 中對其進行定位和檢索。
示例:一個響應式的網格布局-查看示例
<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添加相關的屬性實現的:
.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 后刷新頁面,你會發現所有的縱列都被折疊了。這是因為所有的布局元素都嘗試去填充第一縱列和第一橫列,我們需要進一步為布局元素設定定位信息。一旦你為某個元素聲明了網格信息,那么所有的子元素就都需要在網格中進行定位。
為了適配小尺寸的屏幕,我將從元素定位開始做布局。由于用戶使用小尺寸設備時是從上到下閱讀的,所以我會按照源碼中的內容順序來布局,而對于使用屏幕閱讀器的用戶,我會根據內容的重要性來安排布局順序。在分離源碼結構和布局結構這個新領域,我無需過多擔心視覺效果背后的源碼結構。
.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的寬度時發生一些改變。
@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以及更寬的屏幕重新設計了網格布局。
@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標簽來包裹文章的所有內容,并在文檔底部添加了多個空標簽。
<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是我為原始內容聲明的一個名字,也可以用任何其他的名字。
.main { -webkit-flow-into: article-thread; }
一旦添加了該屬性,那么原始內容就會消失,這是因為你還沒有指定它們的布局位置。
在上面的 HTML 結構中,每個空標簽都擁有一個article-regions的類名,所以我可以讓它們接收原始內容。
.article-regions { -webkit-flow-from: article-thread; }
在這里,我們使用了一個新的屬性flow-from,并匹配了之前為原始內容聲明的名字。如果現在刷新頁面,就會發現內容重新出現在頁面上了。接下來,就可以讓第一個區域“流動起來了”。當內容“流動起來”之后,我們添加 CSS 來指定流動區域。
.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標簽,標簽內部是一張貓的圖片。我希望這張圖片在布局時呈現一種文字環繞的效果。
<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標簽之后的,但它會顯示在文字的上方。
.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屬性即可。
.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。