使用 CSS Grid 實現表單佈局的優勢
表單佈局和設計一直是網頁設計和開發中令人頭疼的一部分。 嘗試過寫 <select>
標籤的樣式,或者在所有瀏覽器中都居中一個 label
的人感觸肯定更深。
在2016年,我寫了一篇文章標題為 "Make Forms Fun with Flexbox"
,文中指出瞭如何使用 Flexbox
佈局解決多種表格佈局的困難。為了展示 HTML
標籤順序的一致性,label
總是跟隨在一個關聯域標籤之後:
<div>
<input id="name" name="name" type="text" />
<label for="name">name</label>
</div>
<div>
<select id="experience" name="experience"><!-- options --></select>
<label for="experience">experience</label>
</div>
<div>
<input id="html" name ="html" type="checkbox" />
<label for="html">HTML</label>
</div>
然後,Flexbox
可以這樣用:
- 如果需要重新定位
label
順序,比如,將文字輸入(input)、選擇框(select) 或者textarea
移動到label
的左邊 - 垂直對齊
label
和filed
還可以使用相鄰的同級選擇器來根據它們的欄位的狀態來新增樣式,例如,當選中複選框時,將粗體應用到 label
上:
input:checked + label {
font-weight : bold;
}
使用 Flexbox
來設計表單的缺點
可是在表單佈局中使用 Flexbox
有很多問題。 Flexbox
建立一個一維佈局,一項需要折行後跟著一項, Field/label
必須放置在帶有 display: flex;
的父級容器元素中,這樣才能保證每一行都換行。
必須要給 label
一個固定的寬度,例如 10em
。 因為如果一個label
內文字超長,文字可能會溢位,使得重置元素寬度或者擠出旁邊的表單元素,無法與其他行的元素對齊。
最後,表單通常會在網格中設計。 我們為何不試試主流瀏覽器支援的 CSS Grid
佈局呢?
相容方式
大多數 CSS Grid
文章都介紹了這些概念,而且也為舊版本的瀏覽器提供了優雅降級。 當佈局有很多裝飾性佈局的時候,這種方法是很理想的——例如,定位頁面內容、頁首、頁尾和選單。就算是少量的舊瀏覽器內展示為線性佈局,頁面也依然是可用的。
表單佈局從功能上來說,會相對重要,因為使用者如果在錯誤的框中輸入資訊會導致資訊錯誤。所以本教程採用的是一種漸進增強的方法:
- 浮動佈局適用於所有的瀏覽器,包括
IE8 +
(不支援Flexbox
)。 - 在所有現代瀏覽器中使用
CSS Grid
來實現表單佈局
下面的例子因為樣式很少,所以直接寫在了 HTML
元素上。 這種方式 BEM
不推薦,但也能保持程式碼可讀,整潔。
我們先寫一些表單的基礎結構。
HTML
一個經典的 HTML
表單結構應該儘量保持簡潔,沒有必要用 <div>
包裹每一個鍵值對。
<form action="get">
<fieldset>
<legend>Your web development skillset</legend>
<div class="formgrid">
<input id="name" name="name" type="text" />
<label for="name">name</label>
<select id="experience" name="experience">
<option value="1">1 year or less</option>
<option value="2">2 years</option>
<option value="3">3 - 4 years</option>
<option value="5">5 years or more</option>
</select>
<label for="experience">experience</label>
<input id="html" name="html" type="checkbox" />
<label for="html">HTML</label>
<input id="css" name="css" type="checkbox" />
<label for="css">CSS</label>
<input id="javascript" name="javascript" type="checkbox" />
<label for="javascript">JavaScript</label>
<textarea id="skills" name="skills" rows="5" cols="20"></textarea>
<label for="skills">other skills</label>
<button type="submit">SUBMIT</button>
</div>
</fieldset>
</form>
唯一的附加元素是 <div class="formgrid">
。 fieldset
元素瀏覽器是無法設定 display: grid
或者 display: flex
的, 所以需要一個外部容器 div
包裹。
表單佈局浮動佈局
在設定初始字型和顏色樣式之後,需要分配浮動佈局:
- 70% 寬度向右浮動
- 30% 寬設定給
label
,向左浮動
/* fallback 30%/70% float layout */
input, output, textarea, select, button {
clear: both;
float: right;
width: 70%;
}
label {
float: left;
width: 30%;
text-align: right;
padding: 0.25em 1em 0 0;
}
複選框和單選按鈕放置在 label
前,然後向左浮動。 它們的內部寬度可以自適應(width:auto) ,但要設定左邊距 30%
進行對齊:
button, input[type="checkbox"], input[type="radio"] {
width: auto;
float: left;
margin: 0.5em 0.5em 0 30%;
}
input[type="checkbox"] + label, input[type="radio"] + label {
width: auto;
text-align: left;
}
這種方式的表單佈局適用於所有瀏覽器,包括 IE8 +
https://codepen.io/SitePoint/pen/bxVaKK
一個不認真的開發人員會認為這樣就完事兒了,但是這個表單其實還有幾個小問題:
- 填充和邊距調整是很不穩定的,在不同瀏覽器中可能表現不一致
- 如果需要更長的標籤文字或不同大小的字型,那麼
CSS
間距就會需要調整 - 在小螢幕尺寸下,
label
會溢位
開始使用網格
Grid
模組為了能夠建立一個有行和列的佈局,新增了 18 個新的 CSS
屬性。
模組增加了18個新的 CSS 屬性,以便建立一個有行和列的佈局。 網格內的元素可以放置在任何行/列,也可以佔多行或者多列,重疊其他元素,設定水平或垂直居中。 聽起來和 Flexbox
很相似,但是:
Flexbox
是一維的。 元素是一個挨著一個出現的,很有可能不會包裹在一個新的"行"中。 常用的案例是選單或者圖片庫
網格是二維的,同時擁有行和列。 如果一個元素對它的單元來說太大,行或列還會相應地變大。佈局頁面或者表單,網格真是再適合不過了。
CSS
網格相比基於表格的佈局, 會更靈活,需要更少的標籤。 相比其他的 CSS
屬性,網格會更加複雜一些,學起來會稍難一點,但是不需要每個屬性和值都記住,可以先了解最基本的網格在元素上的使用:
.container {
display: grid;
}
我們在設計佈局的時候,肯定會需要知道列的數量,大小,以及行和列之間的間隔。 像這樣:
.container {
display: grid;
grid-template-columns: 10% 1fr 2fr 12em;
grid-gap: 0.3em 0.6em;
}
這裡定義了四個列。單位可以用任意單位或者是 fr
。fr
在網格佈局裡剩餘的空間內,進行比例分配。 上面的示例中第二和第三列的總共3fr
。 如果有 600 畫素的水平空間可用:
1fr 等於(1fr / 3fr) * 600px = 200px
2fr 等於(2fr / 3fr) * 600px = 400px
在行之間定義 0.3em
的間隙,並在列之間定義 0.6em
。
.container
所有的子元素現在都屬於網格的格。 預設情況下,第一個子元素將出現在第 1 行第 1 列。 第二個子元素在第 1 行,第 2 列,第 6 個在第 2 行,第 2 列。 使用 grid-template-rows
屬性可以對行進行大小調整,但高度是由內容撐開的。
Grid
的相容性很好。在 Opera Mini
中不可用, 但 IE11
提供了一個較老的補充規範。在大多數情況下,相容設定很簡單:
-
舊版本瀏覽器可以使用
flexbox
,floats
,inline-blocks
或者display: table
。所有的網格屬性都會被忽略。 -
當瀏覽器支援
grid
時,所有的flexbox
,floats
,inline-blocks
和table
佈局屬性會被禁用。
Grid
工具和資源:
MDN Grid Layout
MDN 網格佈局
A Complete Guide to Grid
一個完整的網格指南
Grid by Example
網格例子
Grid “fallbacks” and overrides
網格"後退"和重寫
CSS Grid Playground
網格背景圖
CSS Grid Garden
網格花園
Layoutit!
用它佈局
Firefox
和 chrome
瀏覽器擁有開發工具和視覺化工具,支援網格佈局。
表單網格
為了逐步增強現有的表格,網格程式碼將放置在 @supports
宣告中:
/* grid layout */
@supports (display: grid) {
...
}
在大多數網格佈局中都沒必要這樣寫。 但這個示例需要重置所有的浮動內邊距和外邊距——需要保證這些規則只有在能使用 CSS Grid
的時候才起作用。
這個表單佈局將使用三欄設計:
這個表單的佈局:
label
佔一列- 複選框或者單選按鈕佔一列或者兩列, 右對齊
- 複選框或者單選名稱佔三列
- 其他元素,需要二到三列
外層容器和子元素的樣式:
.formgrid {
display: grid;
grid-template-columns: 1fr 1em 2fr;
grid-gap: 0.3em 0.6em;
grid-auto-flow: dense;
align-items: center;
}
input, output, textarea, select, button {
grid-column: 2 / 4;
width: auto;
margin: 0;
}
grid-column
定義了起始和結束的網格間隙。 間隙是兩個cells
之間的邊際,所以三欄形式佈局有四個間隙:
- 第一列前的第一條間隙
- 第一列和第二列之間的間隙
- 第二列和第三列之間的間隙
- 第三列和右邊的最後一條間隙
grid-column: 2 / 4;
在間隙 2 和 4 之間填充內容,或者說列 2 和列 3 之間。
第一個 HTML
元素是 <input>
。 它佔了第二列和第三列,這意味著第一列(間隙1 或者2)是空的。 因此,預設情況下,label
標籤將下降到第2行,第1列。 然而,通過設定 grid-auto-flow: dense;
; 在 container
中,瀏覽器將嘗試填補網格中的空單元格,然後再進入一個新的行。
複選框和單選按鈕現在可以設定在間隙1到3(1和2列) ,justify-self: end
靠右對齊:
input[type="checkbox"], input[type="radio"] {
grid-column: 1 / 3;
justify-self: end;
margin: 0;
}
網格上的 labels
將自適應於任何一行的單元格。 所以,現在不像是在浮動佈局中需要設定預設的寬度和間距:
label, input[type="checkbox"] + label, input[type="radio"] + label {
width: auto;
padding: 0;
margin: 0;
}
最後,<textarea>
還可以在單元格垂直方向的頂部而不是中心:
textarea + label {
align-self: start;
}
下面是最後一個基於網格的表單佈局:
https://codepen.io/SitePoint/pen/bxVaKK
與浮動不同,在新增不同的字型、尺寸或 labels
時需要調整設計的情況下,佈局不會因為這些小調整而打破。
網格學習
一般新的學習可能需要花幾年才能真正實行,但是 CSS Grid
有很好的支援,當你使用浮動佈局或者彈性佈局佈局很困難的時候,提供了另一種佈局的可能性。 表單是一個合適的例項,寫出來的 CSS
程式碼會很精煉。
如果你想學習另一種 CSS
技能,Grid
應該是你的首選。