高階表單樣式

在本文中,我們將瞭解如何使用 CSS 樣式化那些更難樣式化的表單控制元件型別 — “糟糕”和“醜陋”類別。正如我們在 上一篇文章 中看到的,文字欄位和按鈕非常容易樣式化;現在我們將深入研究如何樣式化更復雜的部分。

先決條件 HTMLCSS 的基本理解。
目標 為了理解表單中哪些部分難以進行樣式設定,以及原因;學習如何自定義它們。

回顧我們之前文章中提到的內容,我們有

不好的地方:某些元素難以設定樣式,需要更復雜的 CSS 或更具體的技巧

醜陋的地方:某些元素無法使用 CSS 徹底進行樣式設定。這些元素包括

首先讓我們談談 appearance 屬性,它對於使以上所有元素更易於設定樣式非常有用。

appearance: 控制作業系統級別的樣式

在之前的文章中,我們提到歷史上,Web 表單控制元件的樣式很大程度上來自底層作業系統,這也是自定義這些控制元件外觀的問題所在。

appearance 屬性是為了控制應用於 Web 表單控制元件的 OS 或系統級樣式而建立的。迄今為止,最實用的值(可能也是你唯一用到的值)是 none。它儘可能地阻止你應用它的任何控制元件使用系統級樣式,並允許你使用 CSS 自行構建樣式。

例如,讓我們看看以下控制元件

html
<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 應用於它們會移除系統級樣式。

css
input {
  appearance: none;
}

以下即時示例展示了它們在你係統中的外觀 - 預設在左側,應用上述 CSS 在右側 (點選此處檢視,如果你想在其他系統上測試的話)。

在大多數情況下,效果是移除帶樣式的邊框,這使得 CSS 樣式設定稍微容易一些,但並不真正必要。在幾個情況下 - 搜尋和單選按鈕/複選框,它變得更加有用。我們現在將看看這些情況。

馴服搜尋框

<input type="search"> 本質上只是一個文字輸入框,那麼為什麼 appearance: none; 在這裡有用呢?答案是 Safari 搜尋框有一些樣式限制 - 例如,你無法自由調整它們的 heightfont-size

這可以使用我們的老朋友 appearance: none; 來解決,它會停用預設外觀

css
input[type="search"] {
  appearance: none;
}

在下面的示例中,你可以看到兩個相同樣式的搜尋框。右邊的搜尋框應用了 appearance: none;,而左邊的沒有。如果你在 macOS 上的 Safari 中檢視,你會發現左邊的搜尋框尺寸不正確。

有趣的是,在搜尋框上設定邊框/背景也會解決這個問題。以下帶樣式的搜尋框沒有應用 appearance: none;,但在 Safari 中不會像之前的示例那樣出現問題。

注意:你可能已經注意到,在搜尋框中,當搜尋值不為空時出現的“x”刪除圖示,在 Edge 和 Chrome 中,當輸入框失去焦點時會消失,但在 Safari 中會保留。要使用 CSS 移除它,可以使用 input[type="search"]:not(:focus, :active)::-webkit-search-cancel-button { display: none; }

樣式設定複選框和單選按鈕

預設情況下,樣式設定複選框或單選按鈕很棘手。複選框和單選按鈕的大小並非旨在透過其預設設計進行更改,並且瀏覽器在嘗試時反應非常不同。

例如,考慮以下簡單的測試用例

html
<label
  ><span><input type="checkbox" name="q5" value="true" /></span> True</label
>
<label
  ><span><input type="checkbox" name="q5" value="false" /></span> False</label
>
css
span {
  display: inline-block;
  background: red;
}

input[type="checkbox"] {
  width: 100px;
  height: 100px;
}

不同的瀏覽器對複選框和 span 的處理方式不同,通常是醜陋的方式

瀏覽器 渲染
Firefox 71 (macOS) Rounded corners and 1px light grey border
Firefox 57 (Windows 10) Rectangular corners with 1px medium grey border
Chrome 77 (macOS)、Safari 13、Opera Rounded corner with 1px medium grey border
Chrome 63 (Windows 10) Rectangular borders with slightly greyish background instead of white.
Edge 16 (Windows 10) Rectangular borders with slightly greyish background instead of white.

對單選按鈕/複選框使用 appearance: none

如前所述,你可以使用 appearance: none; 徹底移除複選框或單選按鈕的預設外觀。讓我們看看這個示例 HTML

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>

現在,讓我們使用自定義複選框設計對它們進行樣式設定。首先,讓我們取消原始複選框的樣式

css
input[type="checkbox"] {
  appearance: none;
}

我們可以使用 :checked:disabled 偽類來更改自定義複選框的外觀,因為它狀態發生變化

css
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: #ddd;
  color: gray;
}

你將在 下一篇文章 中瞭解更多關於這些偽類以及更多內容;上面的偽類執行以下操作

  • :checked - 複選框(或單選按鈕)處於選中狀態 - 使用者已單擊/啟用它。
  • :disabled - 複選框(或單選按鈕)處於停用狀態 - 無法與之互動。

你可以看到即時結果

我們還建立了一些其他示例,以提供更多想法

如果你在不支援 appearance 的瀏覽器中檢視這些複選框,你的自定義設計將丟失,但它們仍然看起來像複選框並且可以使用。

如何處理“醜陋”的元素?

現在讓我們把注意力轉向“醜陋”的控制元件 - 那些非常難以徹底設定樣式的控制元件。簡而言之,這些控制元件是下拉框,複雜的控制元件型別,例如 colordatetime-local,以及面向反饋的控制元件,例如 <progress><meter>

問題是這些元素在不同瀏覽器中具有非常不同的預設外觀,雖然你可以在某些方面對它們進行樣式設定,但它們內部的某些部分實際上無法設定樣式。

如果你準備接受外觀和感覺上的差異,你可以使用一些簡單的樣式來使尺寸保持一致,對背景顏色等進行統一樣式設定,並使用外觀來擺脫一些系統級樣式。

以下示例展示了幾個“醜陋”的表單功能的實際應用

此示例應用了以下 CSS

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 #ccc;
  border-radius: 5px;
}

label {
  margin-bottom: 5px;
}

button {
  width: 60%;
  margin: 0 auto;
}

注意:如果你想在多個瀏覽器中同時測試這些示例,你可以 點選此處檢視(也可以 點選此處檢視原始碼)。

還要記住,我們在頁面中添加了一些 JavaScript 程式碼,這些程式碼列出了檔案選擇器選擇的

如你所見,我們已經成功地使它們在現代瀏覽器中看起來相當統一。

我們對所有控制元件及其標籤應用了一些全域性規範化 CSS,使它們以相同的方式調整大小,採用其父元素的字型等,如之前文章中所述

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;
}

我們還向控制元件添加了一些統一的陰影和圓角,這些控制元件適合這樣做

css
input[type="text"],
input[type="datetime-local"],
input[type="color"],
select {
  box-shadow: inset 1px 1px 3px #ccc;
  border-radius: 5px;
}

在其他控制元件(如範圍型別、進度條和儀表)上,它們只是在控制元件區域周圍新增一個醜陋的框,因此沒有意義。

讓我們討論一下這些控制元件型別的具體情況,並在此過程中重點介紹一些困難。

選擇器和資料列表

在現代瀏覽器中,只要你不想改變預設外觀太多,選擇器和資料列表通常很容易設定樣式。

我們已經成功地使框的基本外觀看起來相當統一且一致。資料列表控制元件本身就是 <input type="text">,因此我們知道這不會有問題。

有兩件事稍微麻煩一些。首先,選擇器的“箭頭”圖示指示它是一個下拉選單,在不同的瀏覽器中有所不同。如果增大選擇框的大小,或者以醜陋的方式調整大小,它也會發生變化。為了在我們的示例中修復這個問題,我們首先使用了我們的老朋友 appearance: none 徹底移除圖示

css
select {
  appearance: none;
}

然後我們使用生成的內容建立了自己的圖示。我們在控制元件周圍添加了一個額外的包裝器,因為 ::before/::after 不適用於 <select> 元素(因為它們的內容完全由瀏覽器控制)

html
<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>

然後我們使用生成的內容生成一個小箭頭,並使用定位將其放在正確的位置

css
.select-wrapper {
  position: relative;
}

.select-wrapper::after {
  content: "▼";
  font-size: 1rem;
  top: 6px;
  right: 10px;
  position: absolute;
}

第二個,稍微重要的問題是,你無法控制單擊 <select> 框以開啟它時出現的包含選項的框。你可以繼承父元素上設定的字型,但無法設定間距和顏色等內容。對於 <datalist> 出現時的自動完成列表,也是如此。

如果你確實需要完全控制選項的樣式設定,你必須使用某種庫來生成自定義控制元件,或者構建你自己的自定義控制元件,或者在選擇器的情況下使用 multiple 屬性,這會導致所有選項都出現在頁面上,從而規避了這個問題

html
<label for="select">Select fruits</label>
<select id="select" name="select" multiple></select>

當然,這可能也不符合你想要的設計,但值得注意!

日期輸入型別

日期/時間輸入型別 (datetime-localtimeweekmonth) 都存在相同的主要問題。實際的包含框與任何文字輸入框一樣容易設定樣式,我們在演示中的外觀也很好。

但是,控制元件的內部部分(例如,用於選擇日期的彈出日曆,用於增減值的微調器)根本無法設定樣式,你無法使用 appearance: none; 來移除它們。如果你確實需要完全控制樣式設定,你必須使用某種庫來生成自定義控制元件,或者構建你自己的自定義控制元件。

注意:在這裡也值得一提的是 <input type="number"> - 它也有一個微調器,你可以使用它來增減值,因此也可能存在同樣的問題。但是,在 number 型別的情況下,收集的資料更簡單,很容易使用 tel 輸入型別來代替,它具有 text 的外觀,但在帶有觸控鍵盤的裝置上顯示數字鍵盤。

範圍輸入型別

<input type="range"> 的樣式很難看。您可以使用以下程式碼完全刪除預設的滑塊軌道,並用自定義樣式替換它(在本例中為細紅色軌道)。

css
input[type="range"] {
  appearance: none;
  background: red;
  height: 2px;
  padding: 0;
  outline: 1px solid transparent;
}

然而,要自定義範圍控制元件的拖動手柄的樣式非常困難 - 要完全控制範圍樣式,您需要使用大量的複雜 CSS 程式碼,包括多個非標準的、特定於瀏覽器的偽元素。檢視 Styling Cross-Browser Compatible Range Inputs with CSS,瞭解 CSS 技巧的詳細說明。

顏色輸入型別

型別為顏色的輸入控制元件並不太糟糕。在支援的瀏覽器中,它們往往只是給您一個帶有小邊框的實心色塊。

您可以刪除邊框,只留下色塊,使用類似這樣的程式碼

css
input[type="color"] {
  border: 0;
  padding: 0;
}

但是,自定義解決方案是獲得任何顯著不同的唯一方法。

檔案輸入型別

型別為檔案的輸入通常是可以的 - 正如您在我們的示例中看到的,建立與頁面其餘部分匹配的東西相當容易 - 如果您告訴輸入這樣做,控制元件的一部分輸出行將繼承父字型,並且您可以以任何您想要的方式設定檔名和大小的自定義列表的樣式;畢竟是我們建立的。

檔案選擇器唯一的缺點是,提供的用來開啟檔案選擇器的按鈕是完全不可樣式化的 - 它無法調整大小或顏色,甚至不會接受不同的字型。

解決此問題的一種方法是利用以下事實:如果您有一個與表單控制元件關聯的標籤,單擊該標籤將啟用該控制元件。因此,您可以使用類似這樣的程式碼隱藏實際的表單輸入

css
input[type="file"] {
  height: 0;
  padding: 0;
  opacity: 0;
}

然後將標籤樣式設定為按鈕,當按下時,將按預期開啟檔案選擇器

css
label[for="file"] {
  box-shadow: 1px 1px 3px #ccc;
  background: linear-gradient(to bottom, #eee, #ccc);
  border: 1px solid rgb(169, 169, 169);
  border-radius: 5px;
  text-align: center;
  line-height: 1.5;
}

label[for="file"]:hover {
  background: linear-gradient(to bottom, #fff, #ddd);
}

label[for="file"]:active {
  box-shadow: inset 1px 1px 3px #ccc;
}

您可以在下面的即時示例中看到上述 CSS 樣式的結果(另請參見 styled-file-picker.html 即時,以及 原始碼)。

儀表和進度條

<meter><progress> 可能是最糟糕的。正如您在前面的示例中看到的,我們可以將它們設定為相對準確的所需寬度。但除此之外,它們真的很難以任何方式設定樣式。它們在彼此之間以及瀏覽器之間不一致地處理高度設定,您可以對背景進行顏色設定,但不能對前景條進行顏色設定,並且對它們設定 appearance: none 會使情況更糟,而不是更好。

如果您想控制樣式,或者使用第三方解決方案(如 progressbar.js),最好建立自己的自定義解決方案。

文章 How to build custom form controls 提供瞭如何使用 HTML、CSS 和 JavaScript 構建自定義設計的 select 的示例。

總結

雖然使用 CSS 與 HTML 表單仍然存在困難,但有一些方法可以解決許多問題。沒有乾淨的、通用的解決方案,但現代瀏覽器提供了新的可能性。目前,最好的解決方案是更多地瞭解不同瀏覽器在將 CSS 應用於 HTML 表單控制元件時對 CSS 的支援方式。

在本模組的下一篇文章中,我們將探討現代瀏覽器為我們提供的不同 UI 偽類,用於在不同狀態下對錶單進行樣式設定。

高階主題