使用純 CSS 實現仿 Material Design 的 input 過渡效果

之前一段時間,基於對 nextgraphql 的調研,再加上本人的興趣,我作了一個站點,也做爲我之後各類技術折騰,實踐以及興趣交匯的試驗田。javascript

最近我須要在個人實驗田使用 jwt 實踐校驗碼的功能。校驗碼,就是指註冊時郵箱或者短信的校驗碼。須要校驗碼則須要有登陸註冊,而在登陸註冊時,爲了多寫一些 CSS,我決定實現一個 Material Design 的表單過渡效果。css

實現效果見 詩詞絃歌 - 登陸html

開始以前,你先看看是否定識如下幾個選擇器。若是不,那麼經過本文你能夠學習到如下幾個選擇器,以及他們的試用場景html5

  • :not(:empty)
  • input:not([value=""])
  • input:valid
  • input:not(:placeholder-shown)

本文地址見 shanyue.tech/post/login-…java

問題概述

至於 Material Design 是什麼樣的效果,如上所示。實現以上效果,能夠簡單把問題歸結爲如下兩點的實現node

  1. 當 input 中沒有值且沒有得到焦點時,hint 信息灰色呈如今 input 框內
  2. 當 input 獲取到焦點或者有值時,hint 信息以動畫形式位移到 input 上方

直接把 html 和 css 代碼貼上來,先來完成簡單的功能瀏覽器

label 置於 input 後邊,方便經過 ~+ 選擇器定位。app

form
  .input-wrapper
    input[type="text"]
    label
  .input-wrapper
    input[type="password"]
    label
複製代碼

CSS 代碼以下,label 初始時經過絕對定位置於 input 的 placeholder 位置,input 得到焦點時的 label 的 CSS Selector 也很容易經過 input:focus + label 肯定ide

label {
  /* 定位到input框中 */
  position: absolute;
  bottom: 10px;
  left: 0;
  font-size: 1.2em;
  color: #ccc;
  transition: all ease 0.3s;
  pointer-events: none;
}

input:focus + label {
  /* 定位到 input 上方 */
  color: #f60;
  font-size: 0.8em;
  transform: translateY(-150%);
}
複製代碼

已經在 20% 的時間內完成了 80% 的工做量,還有剩下 20% 的問題總結以下post

  1. 因被 label 遮擋,沒法在 label 文字區域點擊 input
  2. 得到焦點的 CSS Selector 很簡單,還有一種複雜的狀況是 input 非空值時的 CSS Selector

焦點

pointer-events 用來控制鼠標點擊的行爲,若是要實現透過 label 點擊,能夠設置該屬性爲 none

label {
  pointer-events: none;
}
複製代碼

input:not(:empty)

我條件性反射想到用這個來匹配值非空的 input。但我仔細查了下文檔,發現它是不適用的。

根據 empty-pesudo Selectors 描述爲

The :empty pseudo-class represents an element that has no children at all. In terms of the document tree, only element nodes and content nodes (such as DOM text nodes, CDATA nodes, and entity references) whose data has a non-zero length must be considered as affecting emptiness;

我來大體翻譯一下,若是一個元素, 它的子節點數爲 0,那麼它將匹配到 :empty。這和 input 的 value 風馬牛不相及了。

那如何獲取元素 element 子節點的個數呢? 使用 element.childNodes.length

參考 stackoverflow :not(:empty) CSS selector is not working?

input:not([value=""])

那麼再簡單粗暴些,直接匹配屬性 value 是不就能夠了。

No. 不能夠。input 中的顯示值並不等同與屬性 value。

那這就引出了下一個問題

如何獲取 input 的值

用如下代碼測試一下

<input type="text" id="input">
複製代碼
input.value // ''
input.getAttribute('value') // null

input.setAttribute('value', 4)
input.value // '4',value 值同步過來了
input.getAttribute('value') // '4'

input.value = 3
input.value // '3'
input.getAttribute('value') // '4'
複製代碼

結論:

  1. input 經過 input.value 獲取值,而非 input.getAttribute('value')
  2. 若是 input.value 爲空,那麼能夠經過 input.setAttribute('value', value) 設置值,而且會同時修改 input.value
  3. 若是手動爲 input.value 賦值後,則使用 input.setAttribute('value', value) 無效

那麼匹配值非空的 input,能夠更深理解爲 匹配input.value爲空的input

這說明在純css實現時沒法使用 input:not([value=""]) 做爲選擇器

使用 js

思路很簡單,同步 input.valueinput.getAttribute('value')

<input onkeyup="this.setAttribute('value', this.value);" />
複製代碼

使用 React

受控的 input 組件經過手動控制屬性 value,來設置 input 的值。

這時再結合使用 input:not([value=""]) 選擇器能夠成功控制 input 的過渡效果

而我項目中也是在使用 React,使用這一選擇器能夠很好的知足個人需求

input:valid

在 html5 中,input 的 type 新增瞭如下類型

  • email
  • number
  • search
  • url
  • datetime
  • ...

而且添加了檢驗方式,如是否必須,正則等

  • max/min,最大最小值
  • pattern,正則
  • required,是否必填

這裏引入一個新的選擇器 input:required,匹配全部擁有合法值的 input

咱們能夠對全部 input 添加 required 的標誌,此時 input:valid 的含義即匹配有值時的 input,順利解決問題

可是使用 input:valid 也有一些限制,如如下兩點

  1. 全部的 input 必須是 required,選填的也必須做爲 required,失去了語義化,且提交時會有提示沒法正常提交
  2. 沒法使用 pattern,email 等複雜 input 的校驗

校驗提示觸發時機

formsubmit 事件觸發後,會引起瀏覽器自帶的校驗提示

formsubmit 事件觸發須要知足如下兩個條件

  1. 包裹在 form 下
  2. 點擊form下 input[type="submit"] 或者 button 進行提交

對於 input:valid 的兩點限制,能夠經過把 form 改爲 div 解決,此時沒法有 submit 事件,選填項也能夠正確處理。至於複雜表單校驗,則經過js控制

input:not(:placeholder-shown)

見名思意,:placeholder-shown 此選擇器匹配是否有 placeholder,既然有 placeholder,那麼 input 就沒有 value

它有一個必要條件,placeholder 屬性不能爲空,若是實在沒必要要能夠設置爲空字符串

<input placeholder=" " />
複製代碼

參考


關注公衆號山月行,在這裏記錄了個人技術成長,歡迎交流

歡迎關注公衆號山月行,記錄個人技術成長,歡迎交流
相關文章
相關標籤/搜索