以前一段時間,基於對 next
與 graphql
的調研,再加上本人的興趣,我做了一個站點,也作為我以後各種技術折騰,實踐以及興趣交匯的試驗田。
最近我需要在我的實驗田使用 jwt
實踐校驗碼的功能。校驗碼,就是指註冊時郵箱或者簡訊的校驗碼。需要校驗碼則需要有登入註冊,而在登入註冊時,為了多寫一些 CSS,我決定實現一個 Material Design 的表單過渡效果。
實現效果見 詩詞絃歌 - 登入
開始之前,你先看看是否認識以下幾個選擇器。如果不,那麼通過本文你可以學習到以下幾個選擇器,以及他們的試用場景
:not(:empty)
input:not([value=""])
input:valid
input:not(:placeholder-shown)
本文地址見 shanyue.tech/post/login-…
問題概述
至於 Material Design 是什麼樣的效果,如上所示。實現以上效果,可以簡單把問題歸結為以下兩點的實現
- 當 input 中沒有值且沒有獲得焦點時,hint 資訊灰色呈現在 input 框內
- 當 input 獲取到焦點或者有值時,hint 資訊以動畫形式位移到 input 上方
直接把 html 和 css 程式碼貼上來,先來完成簡單的功能
label 置於 input 後邊,方便通過 ~
與 +
選擇器定位。
form
.input-wrapper
input[type="text"]
label
.input-wrapper
input[type="password"]
label
複製程式碼
CSS 程式碼如下,label 初始時通過絕對定位置於 input 的 placeholder 位置,input 獲得焦點時的 label 的 CSS Selector 也很容易通過 input:focus + label
確定
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% 的問題總結如下
- 因被 label 遮擋,無法在 label 文字區域點選 input
- 獲得焦點的 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'
複製程式碼
結論:
- input 通過
input.value
獲取值,而非input.getAttribute('value')
- 如果
input.value
為空,那麼可以通過input.setAttribute('value', value)
設定值,並且會同時修改input.value
- 如果手動為
input.value
賦值後,則使用input.setAttribute('value', value)
無效
那麼匹配值非空的 input,可以更深理解為 匹配input.value為空的input
這說明在純css實現時無法使用 input:not([value=""])
作為選擇器
使用 js
思路很簡單,同步 input.value
到 input.getAttribute('value')
<input onkeyup="this.setAttribute('value', this.value);" />
複製程式碼
使用 React
受控的 input
元件通過手動控制屬性 value
,來設定 input 的值。
這時再結合使用 input:not([value=""])
選擇器可以成功控制 input 的過渡效果
而我專案中也是在使用 React
,使用這一選擇器可以很好的滿足我的需求
input:valid
在 html5 中,input 的 type 新增了以下型別
- number
- search
- url
- datetime
- ...
並且新增了檢驗方式,如是否必須,正則等
- max/min,最大最小值
- pattern,正則
- required,是否必填
這裡引入一個新的選擇器 input:required
,匹配所有擁有合法值的 input
我們可以對所有 input
新增 required
的標誌,此時 input:valid
的含義即匹配有值時的 input
,順利解決問題
但是使用 input:valid
也有一些限制,如以下兩點
- 所有的 input 必須是
required
,選填的也必須作為required
,失去了語義化,且提交時會有提示無法正常提交 - 無法使用 pattern,email 等複雜 input 的校驗
校驗提示觸發時機
在 form
的 submit
事件觸發後,會引發瀏覽器自帶的校驗提示
而 form
的 submit
事件觸發需要滿足以下兩個條件
- 包裹在 form 下
- 點選form下
input[type="submit"]
或者button
進行提交
對於 input:valid
的兩點限制,可以通過把 form
改成 div
解決,此時無法有 submit
事件,選填項也可以正確處理。至於複雜表單校驗,則通過js控制
input:not(:placeholder-shown)
見名思意,:placeholder-shown
此選擇器匹配是否有 placeholder
,既然有 placeholder
,那麼 input
就沒有 value
。
它有一個必要條件,placeholder
屬性不能為空,如果實在不必要可以設定為空字串
<input placeholder=" " />
複製程式碼
參考
關注公眾號山月行,在這裡記錄了我的技術成長,歡迎交流