2024年4月13日 星期六

自製表單欄位的浮動標題/標籤 (Floating Field Label/Title)

今天有個需求是想要製作一個
"表單欄位的浮動標題/標籤 (Floating Field Label/Title)",
用自己的想法自製完了以後在這篇文中做個紀錄和分享一下。

Floating Field Label/Title 具體是什麼可以參考 Bootstrap 的一個實現:Floating labels
就是一個被設計放在表單欄位 (例如 <input type="text"/>) 中的 Placeholder,
但當要在欄位上輸入內容時,這個 Floating Field Label 會自行地往上移動讓出欄位的空間給
使用者輸入內容。

需求如下:

  1. Floating Label 平常顯示位置在欄位之中。
  2. 欄位必須要能容納 Floating Label 的所有文字,也就是 Floating Label 內容不能被截斷或超出欄位。
  3. 當欄位被處於 focus 狀態 (例如滑鼠點到欄位使其處於擁有焦點狀態) 或
    欄位處於有被輸入了文字 的情況, Floating Label 必須要往上移動讓出欄位空間,
    同時欄位上方必須要能自動根據 Floating Label 的文字多少預留足夠 Floating Label 上移的空間。

首先先直接看最後的成品,再來對其說明:

HTML:

<div>
  <div class="hidden-text">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.
  </div>
  <div class="wrapper">
    <div>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.
    </div>
    <input type="text" placeholder="" />
    <label>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.
    </label>
  </div>
</div>

CSS:

:root {
  --wrapper-width: 300px;
}

.hidden-text {
  visibility: hidden;
  width: var(--wrapper-width);
}

.wrapper {
  position: relative;
  width: var(--wrapper-width);
}

label {
  position: absolute;
  top: 0px;
  pointer-events: none;
  transition: all 0.25s ease;
}

input:not(:placeholder-shown) + label,
input:focus + label{
  top: -100%;
  transition: all 0.25s ease;
}

input {
    width: var(--wrapper-width);
    height: 100%;
    position: absolute;
    top: 0px;
}

input::placeholder {
  opacity: 0;
}

說明:
HTML 中是我們主要的設計結構,下面這一串文字是用來展示的 Floating Label 測示文字,只是一個 Lorem ipsum

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.

注意到在 HTML 上我們的三個 Lorem ipsum 內容是完全一樣的。
然後為了讓各元件的 width 寬度一致,我們在 css 設定了一個 --wrapper-width 變數給各元件共用:

:root {
  --wrapper-width: 300px;
}

想法是這樣的,我們需要一個能夠依照文字內容自行撐高的 <input type="text"/>,
所以我們將它跟一個文字內容放在同一個 wrapper 裡,這裡我們再放了一個 Floating Label 進去,像這樣:

<div class="wrapper">
    <div>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.
    </div>
    <input class="line2" type="text" placeholder=" " />
    <label>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.
    </label>
  </div>

我們利用 <div> 來撐高外層的 .wrapper,然後將 <input> 的高設定成跟 .wrapper 一樣,同時設定  positoin:  absolute; top: 0px; 給 <input> 來讓它可以不影響 .wrapper 的高度。
不要忘了設定 position: relative; 給 .warrper,這樣 .wrapper 才能做為 <input> position:absolute; top 位置的基準。

注意到 <inpup> 設定了一個空字串的 placeholder,這是必須的,之後會說明,其是為了能夠被 :placeholder-shown  CSS 選擇器選到。

有一點可以注意到,因為 <input> 在 HTML 是寫在 <div> 之後,所以 <div> 會被 <input> 遮注而不會被看到,這樣我們就不須要特別設定 css 讓 <div> 看不到。

.wrapper {
  position: relative;
  width: var(--wrapper-width);
}

input {
    width: var(--wrapper-width);
    height: 100%;
    position: absolute;
    top: 0px;
}

在看 Floating Label 的 css 之前,我們先看一下最上方的 .hidden-text :

<div class="hidden-text">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ultricies urna nulla, sed viverra purus elementum ut. In hac habitasse platea dictumst. Integer ut dui ligula. Morbi in velit eu urna finibus elementum id consectetur neque.
  </div>

<div> 的作用是讓 .wrapper 上方有一個預留空間可以讓 Floating Label 移上去,所以用文字將其撐高了以後,我們要讓它在保留空間佔位的情況下將字隱藏起來,在這邊用 visibility: hidden 來達到:

.hidden-text {
  visibility: hidden;
  width: var(--wrapper-width);
}

可以注意到,因為上方的 <div> 和下方 .wrapper 用的文字內容一樣,寬度也設定一樣,所以兩者的高度是一樣的。

接下來就是要來設定 Floating Label,首先,Floating Label 跟 <input> 一樣,必須要不撐高 .wrapper 的空間,所以要設定 position: absolute; top: 0px; 。

再來一個比較特別的地方是,因為 Floating Label 的 HTML 寫在 <input> 之後,會擋到 <input> 讓我們無法點擊到 <input>,所以必須要設定 pointer-events: none; 讓點擊能夠穿特過去。

並且我們給它設定一個動畫效果讓它之後在移位時看起來比較滑順,動畫效果不是必須的,不設定也沒關係 :

label {
  position: absolute;
  top: 0px;
  pointer-events: none;
  transition: all 0.25s ease;
}

最後要來設定移位的 css,首先我們希望 <input> 在被 focus 時,Floating Label 能夠移位上去,這可以用 input:focus + label 選擇器來設定,
但如果 <input> 沒有被 focus ,<input> 中有被輸入文字時,我們不希望 Floating Label 被移位回 <input> 裡,這時之前在 <input> 中設定的 placeholder 就派上用場了,
因為 <input> 設定了 placeholder,在 <input> 中有文字被輸入時,placeholder 就會消失看不見,
此時就可以用 :not(:placeholder-shown) 來選擇 placceholder 看不到時的情況。

所以 CSS 就會像是這樣:

input:not(:placeholder-shown) + label,
input:focus + label {
  top: -100%;
  transition: all 0.25s ease;
}

沒有留言 :

張貼留言