高階表單樣式
在本文中,我們將瞭解如何使用 CSS 為那些較難設定樣式的表單控制元件型別(即“不好”和“醜陋”類別)設定樣式。正如我們在上一篇文章中看到的,文字欄位和按鈕非常容易設定樣式;現在我們將深入研究如何為那些更棘手的部分設定樣式。
回顧我們在上一篇文章中說過的,我們有
不好的:有些元素更難設定樣式,需要更復雜的 CSS 或一些更具體的技巧
- 複選框和單選按鈕
<input type="search">
醜陋的:有些元素無法使用 CSS 徹底設定樣式。這些包括
- 涉及建立下拉小部件的元素,包括
<select>、<option>、<optgroup>和<datalist>。注意:一些瀏覽器現在支援可自定義的 select 元素,這是一組 HTML 和 CSS 功能,它們共同實現了對
<select>元素及其內容的完全自定義,就像任何常規 DOM 元素一樣。 <input type="color">- 日期相關控制元件,例如
<input type="datetime-local"> <input type="range"><input type="file"><progress>和<meter>
讓我們首先討論 appearance 屬性,它有助於使上述所有內容更具可樣式性。
appearance:控制 OS 級別樣式
在上一篇文章中,我們提到,從歷史上看,Web 表單控制元件的樣式很大程度上源於底層作業系統,這也是難以自定義這些控制元件外觀的部分原因。
appearance 屬性的建立是為了控制將哪些 OS 或系統級樣式應用於 Web 表單控制元件。到目前為止,最有用的值(也可能是您唯一會使用的值)是 none。它會盡可能阻止您應用它的任何控制元件使用系統級樣式,並允許您使用 CSS 自己構建樣式。
例如,讓我們來看以下控制元件
<form>
<p>
<label for="search">search: </label>
<input id="search" name="search" type="search" />
</p>
<p>
<label for="text">text: </label>
<input id="text" name="text" type="text" />
</p>
<p>
<label for="date">date: </label>
<input id="date" name="date" type="datetime-local" />
</p>
<p>
<label for="radio">radio: </label>
<input id="radio" name="radio" type="radio" />
</p>
<p>
<label for="checkbox">checkbox: </label>
<input id="checkbox" name="checkbox" type="checkbox" />
</p>
<p><input type="submit" value="submit" /></p>
<p><input type="button" value="button" /></p>
</form>
對它們應用以下 CSS 會刪除系統級樣式。
input {
appearance: none;
}
下面的即時示例向您展示了它們在您的系統中的外觀——左側是預設樣式,右側是應用了上述 CSS 後的樣式。
在大多數情況下,效果是刪除樣式化的邊框,這使得 CSS 樣式更容易一些,但並非必不可少。在某些情況下,例如單選按鈕和複選框,它變得更加有用。我們現在就來看看這些。
搜尋框和 appearance
appearance: none; 值過去在一致地設定 <input type="search"> 元素的樣式時特別有用。沒有它,Safari 不允許對其設定 height 或 font-size 值。然而,Safari 16 及更高版本已不再如此。如果您的瀏覽器支援矩陣包含舊於 16 的 Safari 版本,您可能仍希望顯式地使用 appearance: none; 定位 input[type="search"]。
在搜尋輸入中,當值不為空時出現的“x”刪除按鈕在 Edge 和 Chrome 中失去焦點時消失,但在 Safari 中保留。要透過 CSS 刪除,您可以使用以下規則
input[type="search"]:not(:focus, :active)::-webkit-search-cancel-button {
display: none;
}
使用 appearance 為複選框和單選按鈕設定樣式
預設情況下,為複選框或單選按鈕設定樣式很棘手。複選框和單選按鈕的預設樣式大小不應更改,當您嘗試更改時,瀏覽器的反應會非常不同。有些會增加控制元件的大小,有些則保持控制元件大小不變並在其周圍新增額外的空間。
更好的方法是使用 appearance: none; 完全移除複選框和單選按鈕的預設外觀,然後為其各種狀態新增您自己的樣式。
讓我們來看這個 HTML 示例
<form>
<fieldset>
<legend>Fruit preferences</legend>
<p>
<label>
<input type="checkbox" name="fruit" value="cherry" />
I like cherry
</label>
</p>
<p>
<label>
<input type="checkbox" name="fruit" value="banana" disabled />
I can't like banana
</label>
</p>
<p>
<label>
<input type="checkbox" name="fruit" value="strawberry" />
I like strawberry
</label>
</p>
</fieldset>
</form>
讓我們用自定義複選框設計來設定這些樣式。我們將首先移除原始複選框樣式
input[type="checkbox"] {
appearance: none;
}
然後我們可以使用 :checked 和 :disabled 偽類來改變我們自定義複選框的狀態變化時的外觀
input[type="checkbox"] {
position: relative;
width: 1em;
height: 1em;
border: 1px solid gray;
/* Adjusts the position of the checkboxes on the text baseline */
vertical-align: -2px;
/* Set here so that Windows' High-Contrast Mode can override */
color: green;
}
input[type="checkbox"]::before {
content: "✔";
position: absolute;
font-size: 1.2em;
right: -1px;
top: -0.3em;
visibility: hidden;
}
input[type="checkbox"]:checked::before {
/* Use `visibility` instead of `display` to avoid recalculating layout */
visibility: visible;
}
input[type="checkbox"]:disabled {
border-color: black;
background: #dddddd;
color: gray;
}
您將在下一篇文章中找到更多關於此類偽類的資訊;以上這些偽類執行以下操作
:checked— 複選框(或單選按鈕)處於選中狀態 — 使用者已單擊/啟用它。:disabled— 複選框(或單選按鈕)處於停用狀態 — 無法與其互動。
您可以看到即時結果
我們還建立了幾個其他示例,為您提供更多想法
對於“醜陋”的元素能做些什麼?
現在讓我們將注意力轉向“醜陋”的控制元件——那些真正難以徹底設定樣式的控制元件。簡而言之,它們是下拉框、複雜控制元件型別(如 color 和 datetime-local)以及面向反饋的控制元件(如 <progress> 和 <meter>)。
問題在於這些元素在不同瀏覽器中的預設外觀差異很大,雖然您可以以某些方式設定它們的樣式,但它們內部的某些部分是無法設定樣式的。
如果您願意接受外觀和感覺上的一些差異,您可以使用一些簡單的樣式來顯著改善情況。這包括一致的大小和像 background-color 這樣的屬性的樣式,以及使用 appearance 來移除一些系統級樣式。
請看下面的示例,它展示了許多“醜陋”的表單功能。
您還可以按“播放”按鈕,在 MDN Playground 中執行該示例並編輯原始碼。
此示例已應用以下 CSS
body {
font-family: "Josefin Sans", sans-serif;
margin: 20px auto;
max-width: 400px;
}
form > div {
margin-bottom: 20px;
}
select {
appearance: none;
width: 100%;
height: 100%;
}
.select-wrapper {
position: relative;
}
.select-wrapper::after {
content: "▼";
font-size: 1rem;
top: 3px;
right: 10px;
position: absolute;
}
button,
label,
input,
select,
progress,
meter {
display: block;
font-family: inherit;
font-size: 100%;
margin: 0;
box-sizing: border-box;
width: 100%;
padding: 5px;
height: 30px;
}
input[type="text"],
input[type="datetime-local"],
input[type="color"],
select {
box-shadow: inset 1px 1px 3px #cccccc;
border-radius: 5px;
}
label {
margin-bottom: 5px;
}
button {
width: 60%;
margin: 0 auto;
}
我們向頁面添加了一些 JavaScript,用於列出檔案選擇器選擇的檔案,位於控制元件下方。這是 <input type="file"> 參考頁面上示例的簡化版本
const fileInput = document.querySelector("#file");
const fileList = document.querySelector("#file-list");
fileInput.addEventListener("change", updateFileList);
function updateFileList() {
while (fileList.firstChild) {
fileList.removeChild(fileList.firstChild);
}
const curFiles = fileInput.files;
if (!(curFiles.length === 0)) {
for (const file of curFiles) {
const listItem = document.createElement("li");
listItem.textContent = `File name: ${file.name}; file size: ${returnFileSize(file.size)}.`;
fileList.appendChild(listItem);
}
}
}
function returnFileSize(number) {
if (number < 1e3) {
return `${number} bytes`;
} else if (number >= 1e3 && number < 1e6) {
return `${(number / 1e3).toFixed(1)} KB`;
}
return `${(number / 1e6).toFixed(1)} MB`;
}
“全域性”樣式
在前面的示例中,我們已經很好地讓我們的醜陋控制元件在現代瀏覽器中看起來統一。
我們對所有控制元件及其標籤應用了一些全域性標準化 CSS,使它們以相同的方式調整大小,採用其父字型等,正如上一篇文章中提到的那樣
button,
label,
input,
select,
progress,
meter {
display: block;
font-family: inherit;
font-size: 100%;
margin: 0;
box-sizing: border-box;
width: 100%;
padding: 5px;
height: 30px;
}
我們還在適當的控制元件上添加了一些統一的陰影和圓角
input[type="text"],
input[type="datetime-local"],
input[type="color"],
select {
box-shadow: inset 1px 1px 3px #cccccc;
border-radius: 5px;
}
對於其他控制元件,如範圍型別、進度條和計量器,它們只是在控制元件區域周圍添加了一個醜陋的框,所以這沒有意義。
讓我們討論一下這些控制元件中每種型別的具體情況,並在此過程中突出顯示遇到的困難。
選擇和資料列表
一些瀏覽器現在支援可自定義的 select 元素,這是一組 HTML 和 CSS 功能,它們共同實現了對 <select> 元素及其內容的完全自定義,就像任何常規 DOM 元素一樣。在支援的瀏覽器和程式碼庫中,您不再需要擔心下面描述的 <select> 元素的舊技術。
樣式化資料列表和選擇器(在不支援可自定義選擇器的瀏覽器中)允許可接受的自定義級別,前提是您不希望外觀和感覺與預設值相差太大。我們已經設法使這些框看起來相當統一和一致。資料列表呼叫控制元件無論如何都是一個 <input type="text">,所以我們知道這不是問題。
有兩件事稍微更麻煩。首先,指示它是下拉選單的 select 的“箭頭”圖示在不同瀏覽器中有所不同。如果您增加 select 框的大小或以醜陋的方式調整其大小,它也傾向於改變。為了解決我們示例中的這個問題,我們首先使用我們的老朋友 appearance: none 完全去掉了圖示
select {
appearance: none;
}
然後我們使用生成的內容建立了自己的圖示。我們在控制元件周圍添加了一個額外的包裝器,因為 ::before/::after 不適用於 <select> 元素(它們的內容完全由瀏覽器控制)
<label for="select">Select a fruit</label>
<div class="select-wrapper">
<select id="select" name="select">
<option>Banana</option>
<option>Cherry</option>
<option>Lemon</option>
</select>
</div>
然後我們使用生成的內容生成一個小小的向下箭頭,並使用定位將其放置在正確的位置
.select-wrapper {
position: relative;
}
.select-wrapper::after {
content: "▼";
font-size: 1rem;
top: 6px;
right: 10px;
position: absolute;
}
第二個稍微更重要的問題是,當您單擊 <select> 框開啟它時,您無法控制包含選項的出現框。您可以繼承父元素上設定的字型,但無法設定間距和顏色等屬性。對於 <datalist> 出現的自動完成列表也是如此。
如果您確實需要完全控制選項樣式,您將不得不使用庫來生成自定義控制元件或自己構建。對於 <select>,您還可以使用 multiple 屬性,這會使所有選項都顯示在頁面上,從而避免了這個問題
<label for="select">Select fruits</label>
<select id="select" name="select" multiple>
…
</select>
當然,這可能也不符合您正在追求的設計,但值得注意!
日期輸入型別
日期/時間輸入型別(datetime-local、time、week、month)都存在相同的主要相關問題。實際的包含框與任何文字輸入一樣容易設定樣式,我們在此演示中得到的結果看起來很好。
但是,控制元件的內部部件(例如,用於選擇日期的彈出日曆,可用於增加/減少值的微調器)根本無法設定樣式,並且無法使用 appearance: none; 將它們去除。如果您確實需要完全控制樣式,則必須使用庫生成自定義控制元件或自己構建。
注意:這裡也值得提及 <input type="number"> —— 它也有一個微調器,您可以使用它來增加/減少值,因此可能會遇到相同的問題。但是,在 number 型別的情況下,收集的資料更簡單,並且可以輕鬆地改用 tel 輸入型別,它具有 text 的外觀,但在具有觸控鍵盤的裝置中顯示數字鍵盤。
範圍輸入型別
<input type="range"> 的樣式設定很麻煩。您可以使用以下程式碼完全移除預設的滑塊軌道,並將其替換為自定義樣式(在本例中為一條細紅色軌道)
input[type="range"] {
appearance: none;
background: red;
height: 2px;
padding: 0;
outline: 1px solid transparent;
}
然而,要自定義範圍控制元件的拖動手柄樣式非常困難——要完全控制範圍樣式,您需要使用一些複雜的 CSS 程式碼,包括多個非標準、特定於瀏覽器的偽元素。請檢視 CSS Tricks 上的使用 CSS 樣式化跨瀏覽器相容的範圍輸入,瞭解所需內容的詳細說明。
顏色輸入型別
顏色型別的輸入控制元件還不錯。在支援的瀏覽器中,它們通常會給您一個帶有小邊框的純色塊。
您可以使用以下方法移除邊框,只留下顏色塊
input[type="color"] {
border: 0;
padding: 0;
}
然而,自定義解決方案是獲得顯著不同的唯一方法。
檔案輸入型別
檔案型別的輸入通常沒問題——正如您在示例中看到的,建立與頁面其餘部分很好地融合的東西相當容易——如果告訴輸入這樣做,作為控制元件一部分的輸出行將繼承父字型,並且您可以以任何您想要的方式設定檔名稱和大小的自定義列表;畢竟是我們建立的。
檔案選擇器唯一的問題是,用於開啟檔案選擇器的按鈕完全無法設定樣式——它無法調整大小或顏色,甚至不接受不同的字型。
一種解決方法是利用這樣一個事實:如果表單控制元件關聯了一個標籤,單擊標籤將啟用該控制元件。因此,您可以使用類似以下程式碼隱藏實際的表單輸入
input[type="file"] {
height: 0;
padding: 0;
opacity: 0;
}
然後將標籤樣式化為按鈕,當按下時,它將按預期開啟檔案選擇器
label[for="file"] {
box-shadow: 1px 1px 3px #cccccc;
background: linear-gradient(to bottom, #eeeeee, #cccccc);
border: 1px solid darkgrey;
border-radius: 5px;
text-align: center;
line-height: 1.5;
}
label[for="file"]:hover {
background: linear-gradient(to bottom, white, #dddddd);
}
label[for="file"]:active {
box-shadow: inset 1px 1px 3px #cccccc;
}
您可以在下面的即時示例中看到上述 CSS 樣式的結果。
您還可以按“播放”按鈕,在 MDN Playground 中執行該示例並編輯原始碼。
計量器和進度條
<meter> 和 <progress> 可能是最差的。正如您在之前的示例中看到的,我們可以相對準確地將它們設定為所需的寬度。但除此之外,它們在任何方面都非常難以設定樣式。它們在彼此之間和瀏覽器之間處理高度設定不一致,您可以為背景著色,但不能為前景條著色,並且在它們上設定 appearance: none 只會使情況變得更糟,而不是更好。
如果您想控制這些功能的樣式,或者使用第三方解決方案(例如 progressbar.js),則更容易建立自己的自定義解決方案。
總結
儘管使用 CSS 處理 HTML 表單仍然存在困難,但仍有許多方法可以解決這些問題。沒有簡潔、通用的解決方案,但現代瀏覽器提供了新的可能性。目前,最好的解決方案是更多地瞭解不同瀏覽器在應用於 HTML 表單控制元件時對 CSS 的支援方式。
在本模組的下一篇文章中,我們將探討如何使用為此目的提供的專用現代 HTML 和 CSS 功能來建立完全自定義的 <select> 元素。