前言
在HTML布局中有很多的選擇,同一種表現方式可以使用不同的方法來實現.下面來對四種最常見的布局方式進行闡述和解釋,它們分別是Float,Table,Grid和Flex
Float
第一位出場的就是最年老的Float,"老驥伏櫪,志在千里".作為最早出現的定位方式,為元素賦予了"浮動顯示"的技能,從此,元素可以不跟着文檔的方向隨波逐流,而可以擁有自己的"浮動方向",可以說是CSS里面最常出現的熟人了.
float不僅僅出現在網頁上,事實上它借鑒了印刷行業的特性,參見下圖
圖片下方有文字,此時的圖片就是浮動的,可以想象整個文檔流就是一條河,圖片是一條船,文字是水上漂浮的花瓣,當這艘船放在水里的時候,花瓣是會圍繞在這艘船而不會被這艘船壓在下邊,因為都是"浮動"的.
如上圖所示,當頭像被設置為浮動的時候,介紹的文字是根據頭像大小"可響應"(responsive)的,如果設置為非浮動,也就是絕對定位,當頭像變大了之后,文字便會被遮蓋,就像花瓣感知不到船的存在一樣.
如果float只能用於圖片文字的排版那就太小看它了,float可以很輕松的實現整個頁面的布局
float有四種可選值:
1 none(default):沒有浮動
2 left:向左浮動
3 right:向右浮動
4 inherit:繼承祖上的浮動方式.
還有一個孿生的屬性clear,用於清除float的屬性.
如上圖所示,當SideBar不足以占滿右邊所有內容的時候,Footer便會浮動到圖示的位置,但是Footer並不想這樣放在內容的右邊,它向單獨向下另起一行,如果將其clear屬性設置為both,就可以滿足他的心願.
clear還有別的選擇嗎?當然有,跟float一樣,有四種選擇以上的both是清除左右浮動,自成一行,還有清除 左浮動(left), 右浮動(right), 移除clear屬性(none:該值為默認值),其實還有一個inherit:繼承,IE不兼容該屬性(在IE11上進行測試依然不兼容).
如果父元素只包含屬性為float的子元素,那么該父元素的的高度將會為0,這時候需要使用到clear
方法1 將文字元素設為block
這樣可以避免父元素的高度為0的尷尬,但是,為每個文本設置塊太"貴"了,並且還需要調整兩個文本間的margin來使段落看起來間隔自然
方法2 在浮動子元素之后,在父元素閉合標簽之前清除浮動屬性.
1 添加一個空div
<div class="container"> <image src="http://via.placeholder.com/350x150"></image> <image src="http://via.placeholder.com/150x100"><image> <div class="clearfloat"></div> </div>
將其屬性設置為clear:both
img { float:left; } .container { width:500px; } .clearfloat { clear:both; }
最后可以看到container已經有了寬高,為子元素寬高的和
2 使用:after偽類選擇器
<div class="container">
<image src="http://via.placeholder.com/350x150"></image>
<image src="http://via.placeholder.com/150x100"><image>
</div>
img { float:left; } .container { width:500px; } .container:after { content:'.'; visibility:hidden; display:block; height:0; clear:both; }
3 使用overflow屬性
值 | 描述 |
---|---|
visible | 默認值。內容不會被修剪,會呈現在元素框之外。 |
hidden | 內容會被修剪,並且其余內容是不可見的。 |
scroll | 內容會被修剪,但是瀏覽器會顯示滾動條以便查看其余的內容。 |
auto | 如果內容被修剪,則瀏覽器會顯示滾動條以便查看其余的內容。 |
inherit | 規定應該從父元素繼承 overflow 屬性的值。 |
在使用的時候需注意,雖然overflow不需要引入新的空標簽,但是會出現子元素內容被裁減或顯示滾動條.
結果是父元素有了寬高,高是子元素高的和,但是寬是繼承來的寬(如果沒有顯式設置width,默認為100%)
<div class="container"> <img src="http://via.placeholder.com/350x150"></img> <img src="http://via.placeholder.com/150x100"></img> </div>
img { float:left; } .container { overflow: hidden; }
如果想讓浮動元素進行分組有什么辦法呢?比如將下列不同顏色的分為一組
方法1 在需要分割元素后添加一個空元素,然后清除float屬性
img { float:left; } .container { overflow: hidden; } .clearfloat { clear:both; }
方法2 將同一個顏色的包裹在同一個group中,清除group的浮動的屬性
img { float:left; } .container { overflow: hidden; } .group { clear:both }
在使用float屬性的時候有一些bug傳說:
1 overflow:如果圖片超出浮動容器,會影響其他浮動容器的浮動布局.在IE11上進行了測試,已修復,會將Image裁切到容器寬度
2 雙邊距,在具有浮動屬性的元素設置margin,值會翻倍,在IE11已修復.
3 3px Jog:text與具有浮動屬性的元素之間有3px的左間隔 在IE7被修復
4 底邊距bug 父元素會無視子元素的margin-bottom屬性,IE11已修復
* 參考網址及截圖來源CSS Tricks
Table
Structure:
表結構: <thead><tfoot><tbody>
thead和tfoot(如果有)需要盡早的列在前面,而不是將tfoot放在table標簽的末尾,這么做的理由是讓table的結構一目了然.tbody就是表數據內容.
單元格:<th> --> tabular headers <td> --> tabular data <tr> -->table row
<th>雖然有header,但是完全不影響它的位置,可以放在表結構的任何地方,只要你認為這個單元格重要到可以成為"頭".比如這樣:
附屬物: <caption> <col>一個沒有內容,定義列屬性的標簽(對齊align valign,寬度,顏色等) <colgroup> 列集合,跟<col>聯合使用的定義表格的展現方式
<col>和<colgroup>必須在<caption>之后,任意的表結構或<tr>之前定義好.不過該屬性在HTML5中已經不支持了.
<colgroup> <col span="2" style="background-color:red"> <col style="background-color:yellow"> </colgroup>
以上代碼的意義是該列表集合由兩部分組成,前兩列的背景顏色為紅色,后一列的顏色為黃色
Style:
默認表中每一行都有2px的間隔:
collapse屬性去除間隔
table { border-collapse: collapse; }
"合並單元格"是excel中很常見的操作,那么在table布局中如何實現呢?
<th colspan="2"> 表示占兩列 <td rowspan="2"> 表示占兩行,結合這兩個屬性就可以實現需要的布局.
可以將其拆分成以下的元素(colspan,rowspan)進行設置
表格的寬度有三種情況,
1 表格寬度為內容寬度 (內容寬度小於容器寬度)
2 剛好占滿整個容器 (內容寬度等於容器寬度)
3 超出容器大小 (內容寬度大於容器寬度)
使用 white-space:nowrap 設置文字超過容器大小后是否換行(默認換行)
Table的用武之地:
需要表格化展示數據的時候(tables are for tabular data),常見的有:日程表,價格表,屬性表,得分表,員工表,財務數據,日歷,營養表.
但是 將Table用於布局是不符合語義的,是一種hack的方法
1 HTML標簽必須有意義,就像之前說的,table只能用於表格化展示數據.
2 確保網站的可訪問性,其中一個便是屏幕閱讀,屏幕閱讀是從左往右,從上到下,意味着整個網站變成了視覺上的支配而不是可訪問性的支配.而且還會宣布表格的開始,比不用更糟糕
3 源代碼的順序依賴, 為了實現Table布局,會在更重要的內容之前聲明Table,導致SEO的情況並不好.
再一次但是有時候還是會使用Table來布局,比如HTML Email,他需要在各種的設備上運行,包括老舊的設備,一些更現代的布局方式會造成兼容性的問題,通過表格被視為最安全的方式.
Tables are for and only for tabular data
擴展:還有一個table的崇拜者模仿table的表現方式
display: table /* <table> */ display: table-cell /* <td> */ display: table-row /* <tr> */ display: table-column /* <col> */ display: table-column-group /* <colgroup> */ display: table-footer-group /* <tfoot> */ display: table-header-group /* <thead> */
注意,沒有<th>的替代,且以上都是語義值,並不是實際的標簽
Flex
這是我研究最多的布局方式,真是特別的靈活,可以很快速的設置我想要的方式,而且自適應也做的比較好,響應式布局也能用它來實現,不得不說是四劍客當中實力很強的選手了.
Characteristics:
1 可以從任何方向布局-->flow direction,包括leftwards,rightwards,downwards, upwards(請注意,只能選擇一個方向)
2 可以重建視覺順序(visual order)而不影響文檔中的順序, 包括 Reverse: row-reverse, column-reverse Rearrangement: order屬性,這樣的好處是可以保持網頁的可讀性(accessibility).
3 兩種方式布局 1 線性的(no-wrap),只有一條主軸(main axis) 2 折疊換行的(wrapped),包括主軸和交叉軸(cross axis)
4 彈性的size,可以充分利用剩余空間
5 能夠分別設置主軸和副軸的對齊方式
6 在保留副軸的情況下在主軸下動態的折疊或展開
Concept:
最核心的就是下面這一張圖,可以將flex的規則包括80%.(圖片來自W3C Candidate Recommendation)
在container上設置 dispaly: flex; ,那么所有屬於他的items(所有的in-flow children elements)都會按照相同的高度進行flex布局.
Container
1 display: flex或者inline-flex 設置為"塊"級還是行內級容器,需要注意的是,這里的"塊"只針對container,至於它的item只是flex-level,不是block不能設置以下屬性(設置了無效):
1 float和clear
2 vertical-align,可以用align-self代替
3 ::first-line和::first-letter偽類選擇器,flex布局沒有第一行或第一個letter的概念
2 flex-flow:包含1 flex-direction: row | row-reverse | column | column-reverse
, flex-wrap: nowrap | wrap | wrap-reverse
3 justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly
這里重點說一下space-between,space-around和space-evenly.一張圖表示(修改自 CSS-Tricks)
4 align-items: flex-start | flex-end | center | baseline | stretch
跟justify-content的對齊是相對的,前者對應豬豬,此對應副軸,這里有一個baseline的概念,先按下不表
5 align-content: flex-start | flex-end | center | space-between | space-around | stretch;
如果有多行(flex-wrap設置為wrap時)每一行的對齊方式,如果只有一行則失效
Item:
item就是彈性容器內容的內容流,只要在流里(in-flow)的都是item,都能受flex控制
1 flex:包括三個屬性,每個屬性都值得一講
1 flex-grow: 如果一行之后還有剩余空間,那么會按照每個item設置的grow進行擴展,此屬性無單位,表示所占的權重,如果三個item設置的flex-grow均為1,1,1且未wrap,也沒有其他item了,那么每一個會在除開自己的寬度之后的剩余空間每個占1/3.默認為0
2 flex-shrink: 在必要時收縮,默認為1,可以縮小至最小值
3 flex-basis: grow和shrink之前的基礎值,默認為auto<length> | auto
以上三個屬性不要分開用,因為會有部分屬性未設置的隱患,使用flex:統一設置,未設置的為默認值.
flex基本值有4個:
flex:initial | flex:0 1 auto |
flex:auto | flex:1 1 auto |
flex:none | flex:0 0 auto |
flex:一個正值 | flex:正值 1 0 |
2 align-self: 表示對齊方式,跟在container上設置align-items
是一樣的, auto | flex-start | flex-end | center | baseline | stretch
3 order: 表示顯示的順序,可以在不改變文檔書寫順序的情況下改變視覺呈現順序,使頁面具有更好的"可訪問性"(Accessible),默認值為0 可以為負值.
到目前為止所有的屬性已經介紹完了,下面補充一點與flex有關的知識
1 什么是匿名item?
<div style="display:flex"> <!-- flex item: block child --> <div id="item1">block</div> <!-- flex item: floated element; floating is ignored --> <div id="item2" style="float: left;">float</div> <!-- flex item: anonymous block box around inline content --> anonymous item 3 <!-- flex item: inline child --> <span> item 4 <!-- flex items do not split around blocks --> <q style="display: block" id=not-an-item>item 4</q> item 4 </span> </div>
注意打引號的item4,它是一個"block",flex布局不會在block進行item的拆分(不屬於item了),所以"item4"換了行,如果不是"block"的話,"item 4"會接着在第一個item 4右面出現.
2 flex布局是如何確定寬度和高度的?( Line Length Determination )
1 definite size: 定義為不根據所在的布局而改變的size值,例如顯式設置height和width:100px.
2 max(min):最大最小內容約束,這也是item使用grow和shrink的基礎.
3 如果都沒有的話,除去container的margin,padding,border后的值為可用的值(這個值可能是無限大的"infinite",比如高度)
3 container的baseline基線是什么東西?
在之前講align-items對齊方式的時候講過baseline,定義為:
1如果有多行flex lines,那么baseline就是多個lines共享的集合
2 如果container只有一行,包含超過一個item,首尾item中定義了對齊方式,默認是content,text為字的底邊的方式對齊
Grid
/** * 定義grid item空間(space) */ #grid { /** * 兩列 * 1. 第一列寬度為內容的大小 * 2. 第二列占據剩下的空間 * * 三行 * 3. 第一行高度為內容大小 * 4. 中間行占據剩下的空間 * 5. 最后一行高度為內容大小 */ display: grid; grid-template-columns: /* 1 */ auto /* 2 這個1fr是不是很像flex布局中的flex-grow?*/ 1fr; grid-template-rows: /* 3 */ auto /* 4 */ 1fr /* 5 */ auto; } /* 定義每個grid item所在的位置 */ #title { grid-column: 1; grid-row: 1; } #score { grid-column: 1; grid-row: 3; } #stats { grid-column: 1; grid-row: 2; align-self: start; } #board { grid-column: 2; grid-row: 1 / span 2; } #controls { grid-column: 2; grid-row: 3; justify-self: center; }
HTML代碼
<div id="grid"> <div id="title">Game Title</div> <div id="score">Score</div> <div id="stats">Stats</div> <div id="board">Board</div> <div id="controls">Controls</div> </div>
結果示意圖:圖片來自W3C Candidate Recommendation
表格布局就是將內容變成表格一樣的東西,二維,包含水平和垂直(可以與flex中的main,cross對應理解)
Characteristics:
1 固定的,靈活的和基於內容的追蹤size功能
2 使用正(向前),負(向后)的坐標值來顯式設置item位置.
3 自動放置項目到空白區域(包括重新排序時)
4 空間敏感的重復跟蹤(track),自動添加行列適應內容
5 通過margin,gutter(row&column gap),對齊屬性控制spacing和alignment
Concept:
1 block axis 列所(column axis)在的line1,2,3就是在block axis上
2 in-line axis 行(row axis)所在的1,2,3,4就是在in-line axis上
3 grid line,水平和垂直分割線,上圖中的line1,2,3,4和line1,2,3,索引值自動生成,可以在grid item中進行引用,定位.下例表示占據H(右)B空間的item1
用line(線)的概念而不是row和column
#item1 { grid-column: 2; /*從列的line2到line3(默認占據一個單位)*/ grid-row-start: 1; grid-row-end: 2; } /*從行的line1到line2*/
named line:可以通過設置[item-start][item-end]來進行與上例相同的定位,但是需要在container中進行對應的修改
#grid { display: grid; grid-template-columns: 150px [item1-start] 1fr [item1-end]; grid-template-rows: [item1-start] 50px 1fr 50px [item1-end]; } #item1 { grid-column: item1-start / item1-end; grid-row: item1-start / item1-end; }
4 grid track:相鄰grid lines之間的距離,每一個grid track會交給一個sizing函數,會計算出行列的寬高.鄰近的grid track會被gutter(column&row gap)打斷.
5 grid cell:grid item能引用的最小單位(單元格)
6 grid-area是一個邏輯空間(如上例的HHABFF),用來存放一個或多個items的.但是必須是連續的,只能用4條lines來划出這個area,可以在grid container的grid-template-area中顯式命名,也可以隱式的與line的值綁定,grid item通過grid-property(grid-area)屬性來將item放到對應的area中
#item1 { grid-area: A } #item2 { grid-area: B;align-self: start;} #item3 { grid-area: B;justify-self:end;align-self:end}
item1放入A中,item2和item3都放入B中,兩者根據不同的align和justify位置有所區分.
Grid Container:
main { grid: "H H " 50px "A B " 1fr "F F " 50px / 150px 1fr; }
這是最簡潔和形象的方式,上圖表示了三行兩列區域名分別為ABHF的內容,第一列150px,第二列占據剩余空間,第一行和第三行分別為50px,第二行占據剩余空間.(fr: fraction 分數)
1 display:跟flex一樣,有grid和inline-grid的區分,以及item不能使用的屬性也跟flex item一致
2 grid-template-columns, grid-template-rows
grid-template-columns: 100px 1fr max-content minmax(min-content, 1fr) repeat(2, 1fr); /* 5條lines創建了, * 1 起始位置 * 2 距離起始位置寬度為100px * 3 距離第二條line1/4剩余寬度 * 4 距離第三條最大item寬度 * 5 距離第四條不小於最小item寬度,不大於1/4剩余寬度
* 6 距離第五條分別為1/4,1/2的兩條lines * 為了避免超出container的寬度,最好在item中設置彈性值,比如fr.
* 也可根據上面提到的named line來自定義lines名稱創建行列 */
有一個神奇的clamp a grid area:當grid-area部分超過了grid的限制,那么span會被壓縮到最后一個line,如果全部都超過了,那么會在已有的限制上加1,並都壓縮至那個1中
/**如果grid只支持1000個tracks/ .grid-item { grid-row: 500 / 1500;/*部分超出*/ grid-column: 2000 / 3000;/*全部超出*/ } /*保留未超過部分,將超過的部分加1;如果全部超過,則都壓縮至最后一個1中*/ .grid-item { grid-row: 500 / 1001; grid-column: 1000/1001 }
3 grid-template-area: 將自定義命名的item按順序放置
#grid { display: grid; grid-template-areas: "head head" "nav main" "foot ...." } #grid > header { grid-area: head; } #grid > nav { grid-area: nav; } #grid > main { grid-area: main; } #grid > footer { grid-area: foot; }
4 grid-template將2,3均包括
grid-template: auto 1fr / auto 1fr auto;/*設置了row和column,template-area為none*/
grid-template: [header-top] "a a a" [header-bottom]
[main-top] "b b b" 1fr [main-bottom]
/ auto 1fr auto;
上面代碼的結果為:(圖片來自W3C Candidate Recommendation)
5 在grid-column&row屬性中還有一個比較重要的就是grid-auto-column&row,設置隱式的大小
#grid { /*template只包含了一行一列,超出的部分則按照auto來設置大小*/ display: grid; grid-template-columns: 20px; grid-auto-columns: 40px; grid-template-rows: 20px; grid-auto-rows: 40px; } #A { grid-column: 1; grid-row: 1; } #B { grid-column: 2; grid-row: 1; } #C { grid-column: 1; grid-row: 2; } #D { grid-column: 2; grid-row: 2; }
圖片來自(W3C Candidate Recommendation)
6 grid-auto-flow:定義自動的流向,[ row | column ] || dense
row和column就是定義那個方向自動"流",dense代表密集排布,即如果后面的item足夠小能夠將前面留下的空白(hole)給補上,那么會將后面的item提到前面,實現密集型排布(可能會影響順序),如果不設置,默認為sparse,稀疏排布.
7 grid: grid-template | grid-template-rows / [auto-flow &&dense?] grid-auto-column | [auto-flow &&dense?] grid-auto-rows / grid-template-columns, 盡量使用grid而不是分別使用各自的屬性.
grid: auto-flow 1fr / 100px; /*相當於*/ grid-template: none / 100px; grid-auto-flow: row; grid-auto-rows: 1fr; grid-auto-columns: auto;
8 間距和對齊: 對齊屬性包括align-items justify-content align-content都跟flex一樣.間距屬性: row-gap|column-gap|gap,當然在對齊中的 space-around|space-between|space-evenly對gap有影響.
Grid-Item
1 grid-placement-priorities: 要注意這里line(線的概念)
.one { grid-area: main;/*命名main,放在container定義的template對應位置*/; } .two { grid-column: 2; grid-row: 3; /*相當於grid-area: 3/2 放在行3列2的位置,默認span為1*/ } .three { grid-row: 2 / span 5;/*從第二行開始span5行到第七行結束*/ } .four { grid-row: span 5 / 7;/*跨5行,到第七行為止(可以推導出從第二行開始)*/ } .five { grid-column: first / middle;/*從first列到middle列*/ } .six { grid-area: auto;/*自動布局*/ } .seven { grid-area:span 3/ span 2;/*跨3行2列,這是隱式的自動布局,受container的grid-auto-flow控制*/ }
有一個絕對定位值得一提,在container中設置了position:relative,那么在item中使用position:absolute,可以對在所占的grid-area中進行絕對定位:
.abspos { grid-row-start: 1; grid-row-end: span 2; grid-column-start: 3; grid-column-end: auto; /* 直到最右邊的line即5th line */ position: absolute;/*設置absolute*/ /*設置相對定位*/ top: 70px; bottom: 40px; left: 100px; right: 30px; }
2 對齊: column軸:align-self(相當於container中的align-items)跟flex布局的概念一樣
Grid和Flex的聯系:
看到這兩者之間有很多共同的屬性(名字都一模一樣),其實,Grid是為了更好的使用Flex,Flex只能在一個方向上具有彈性,row或者column但是Grid可以在row和column上都具有彈性.兩者結合起來就可以實現任何方向任何顆粒度的彈性化.比如,想要設置整個Grid的背景圖為拉伸的,但是所有的item垂直居中,那么可以這樣來實現:
.grid { align-items: stretch; } .griditem { display: flex; align-items: center; }
Grid做layout,Flex做component