Like Share Discussion Bookmark Smile

J.J. Huang   2019-05-31   Web   瀏覽次數:

Web - 第二十二章 | HTML 表單資料驗證(一)

看了這麼多文章,做了這麼多實作和筆記,發現每一章節都非常重要,任何小部分都不該忽略到,因為你不知道在什麼時候會用到它。這邊將會開始做表單驗證的部分,這在網路上有很多套件可以使用,但是還是建議看過這篇文章再去用套件,會更加了解其運作方式。

表單驗證幫助我們確保用戶以正確格式填寫表單資料,確保提交的資料能使我們的應用程序正常工作。

什麼是表單資料校驗?

訪問任何一個帶註冊表單的網站,你都會發現,當你提交沒有輸入任何訊息的表單時,註冊頁面都會給你提交一個反饋,它告訴你提交了錯誤的資料,這些訊息可能看起來像下面這樣的:

  • “該字段是必填的”(該字段不能留空)
  • “請輸入你的電話號碼,它的格式是:xxx-xxxx”
  • “請輸入一個合法的郵箱地址”
  • “你的密碼長度應該是8至30位的,並且至少應該包含一個大寫字母、一個符號以及一個數字”

這就是表單驗證 ——當你向Web應用提交資料時,應用會校驗你輸入的資料是否是正確的。如果驗證通過,則這些資料可能會被保存至資料庫中或者執行下一步操作,如果校驗未通過,則Web應用會提示你有錯誤的資料,並且一般都會明確的告訴你錯誤發生在哪裡。表單驗證可以通過許多不同的方式實現。

事實上,沒有人願意填寫表單——很多證據表明,用戶對填寫表單這件事情都感到很煩惱,如果他們在填寫表單的過程中遇到一些自己無法理解的問題,通常都會導致他們直接離開你的Web應用,簡而言之,表單是一個很煩人的東西

我們希望填寫我們的表單時,越簡單越好。所以,我們也需要堅持進行資料校驗,這有三個最主要的原因:

  • 我們希望以正確的格式獲取到正確的資料 ——如果用戶提交的資料格式不正確或者資料不正確,我們的應用可能無法正常的運行,所以,當用戶輸入了資料不正確時,我們應該明確的告訴用戶,哪裡出了錯誤;
  • 我們希望保護我們的用戶 ——如果他們真的輸入了類似123456這樣的簡單的賬戶密碼,亦或是壓根就不設置密碼,這會對他們的賬戶安全構成很大的威脅,這是我們不希望看到的;
  • 我們希望保護我們自己 ——有很多不正確或者不安全的資料提交都可能導致我們的應用崩潰。

不同類型的表單資料校驗

在 Web 中,你可能會遇見各種不同的表單驗證:

  • 客戶端驗證發生在瀏覽器端,表單資料被提交之前,這種方式相較於伺服器端校驗來說,用戶體驗更好,它能實時的反饋用戶的輸入校驗結果,這種類型的校驗可以通過下面這些方式實現:
    • JavaScript 校驗,這是可以完全自定義的實現方式;
    • HTML5 內置的校驗,這不需要JavaScript ,而且性能更好,但是不能自定義校驗過程。
  • 伺服器端校驗則是發生在瀏覽器提交資料並被伺服器端程序接收之後——通常伺服器端校驗都是發生在將資料寫入資料庫之前,如果資料有錯誤,則會直接從伺服器端返回錯誤消息,並且告訴瀏覽器端發生錯誤的具體位置和原因,伺服器校驗不像瀏覽器端校驗那樣有好的用戶體驗,但是對於伺服器端應用來說,它又是必須的——這是你能保證資料正確性的最後一步了,在這之後,資料將被持久化至資料庫。當今所有的服務端框架都提供了資料校驗與清洗功能。

注:在真實的項目開發過程中,開發者一般都傾向於使用客戶端校驗與伺服器端校驗的組合校驗方式以更好的保證資料的正確性與安全性。


使用內置表單資料驗證

HTML5一個特別有用的新功能就是,可以在不寫一行腳本代碼的情況下,即對用戶的輸入進行資料校驗,這都是通過表單元素的校驗屬性實現的,這些屬性可以讓你定義一些規則,用於限定用戶的輸入,比如某個輸入框是否必須輸入,或者某個輸入框的字符串有最大長度限制,或者某個輸入框必須輸入一個數字、郵箱地址等,如果表單中輸入的資料都符合這些限定規則,那麼表示這個表單校驗通過,否則則認為校驗未通過。

  • 當一個元素校驗通過時:

    • 該元素將可以通過CSS偽類 :valid 進行特殊的樣式化;
    • 如果用戶嘗試提交表單,如果沒有其它的控制來阻止該操作(比如JavaScript即可阻止提交),那麼該表單的資料會被提交。
  • 如果一個元素未校驗通過:

    • 該元素將可以通過CSS偽類 :invalid 進行列表的樣式化;
    • 如果用戶嘗試提交表單,瀏覽器會展示出錯誤消息,並停止表單的提交

input元素的驗證約束— starting simple

1
2
3
4
5
6
7
input:invalid {
border: 2px dashed red;
}

input:valid {
border: 2px solid black;
}
1
2
3
4
5
<form>
<label for="choose">你喜歡香蕉還是櫻桃?</label>
<input id="choose" name="i_like">
<button>Submit</button>
</form>

required屬性

最簡單的HTML5驗證功能是required屬性—如果要使輸入成為必需的,則可以使用此屬性標記元素。當設置此屬性時,如輸入為空(輸入也將被視為無效),該表單將不會提交(並將顯示錯誤消息)。

1
2
3
4
5
<form>
<label for="choose">你喜歡香蕉還是櫻桃?</label>
<input id="choose" name="i_like" required>
<button>Submit</button>
</form>

在驗證失敗時input 元素會有一個亮紅色的虛線邊框, 在驗證通過時會有一個更微妙的黑色邊框。

使用正則表達式驗證

,常用的驗證功能是pattern屬性,以Regular Expressions作為value值.正則表達式(regex)是一個可以用於匹配以字符組成的文本字符串的模式,所以它們是理想的表單驗證器(以及JavaScript中其它許多的用途)。

正則表達式相當複雜,據我所知,他複雜到人家都出書來做教學,我對這個也沒有很熟和了解,都是需要的時候上網找的;網路上也提供很多驗證的網站。類似:Rubular

下面是一些例子,讓你對它們的工作原理有個基本的了解:

  • a—匹配一個字符a(不能匹配baa等等.)
  • abc—匹配a,其次 b,最後by c.
  • a*—匹配字符a, 0個或者多個( +代表至少匹配一個或者多個).
  • [^a]—匹配一個字符,但它不能是a.
  • a|b—匹配一個字符 a或者b.
  • [abc]—匹配一個字符,它可以是abc.
  • [^abc]—匹配一個字符,但它不可以是abc.
  • [a-z]—匹配字符範圍 a-z且全部小寫(你可以使用[A-Za-z]涵蓋大小寫,或[A-Z]來限制必須大寫).
  • a.c—匹配字符 a,中間匹配任意一個字符,最後匹配字符 c.
  • a{5}—匹配字符 a五次.
  • a{5,7}—匹配字符 a五到七次,不能多或者少.

你也可以在這些表達式中使用數字和其他字符, 例如:

  • [ -] — 匹配一個空格或者虛線.
  • [0-9] — 匹配數字範圍0~9.

你可以任意地組合這些,你可以任意指定不同的部分:

  • [Ll].*k—匹配一個大寫L或者小寫的l,之後匹配一個或多個任意類型的字符,最後匹配一個小寫字母k.
  • [A-Z][A-Za-z' -]+ — 一個大寫字母后面跟著匹配一個及以上的大小寫字母或者中劃線或者撇號或者空格. 這個可以用於校驗英語會話中城市或城鎮名, 但這需要首字母以大寫開頭,不包括其他字符,例如來自英國的Manchester, Ashton-under-lyne, 以及Bishop’s Stortford等.
  • [0-9]{3}[ -][0-9]{3}[ -][0-9]{4}—簡單的匹配一個美國內的電話號碼—三個數字0 -9,後面跟著一個空格或者中劃線,之後匹配三個數字0 -9,再跟著一個空格或者中劃線,最後跟著四個數字0 -9 .但實際情況可能更加複雜,因為有些人會給號碼加上括號什麼的,這裡的表達式只是用來做一個簡單的演示.
1
2
3
4
5
<form>
<label for="choose">你喜歡香蕉還是櫻桃?</label>
<input id="choose" name="i_like" required pattern="櫻桃|香蕉">
<button>Submit</button>
</form>

該<input>元素接受兩個值中的一個:字符串”香蕉”或者字符串”櫻桃”。

注:一些<input>元素類型不需要pattern屬性進行驗證.指定特定email類型就會使用匹配電子郵件格式的正則表達式來校驗(如果有multiple屬性請用逗號來分割多個郵箱).進一步來說,字段url類型則會自動校驗輸入的是否為一個合法的鏈接。
注:該<textarea>元素不支持pattern屬性。

強制條目的長度

所有文本框(<input>或<textarea>)可以強制使用minlength和maxlength屬性.如果值小於該字段minlength的值或大於maxlength值則無效.瀏覽器通常不會組織用戶在文本字段中輸入比預期更長的值,但是可以使用這種細粒度的控件來強制。

在數字條目中(ie <input type=”number”>),該min和max屬性也能強制驗證長度。如果條目中的長度小於min屬性提供的值或大於max屬性的值,該條目則無效。

1
2
3
4
5
6
7
8
9
10
11
12
13
<form>
<div>
<label for="choose">你喜歡香蕉還是櫻桃?</label>
<input id="choose" name="i_like" required minlength="2" maxlength="2">
</div>
<div>
<label for="number">你想要幾個?</label>
<input type="number" id="number" name="amount" value="1" min="1" max="10">
</div>
<div>
<button>Submit</button>
</div>
</form>
  • 這裡我們看到text條目的屬性minlength和maxlength都為2 —這香蕉和櫻桃的長度都為2,輸入少於這個長度的字符顯示無效,並且在瀏覽器不能輸入超過該限制的長度。
  • 我們同時也能讓number條目數值限制在min為1和一個max為10中—輸入超出範圍則顯示無效,並且你將無法使用遞增/遞減箭頭將該值改變到此範圍之外。

注:<input type=”number”>(或者其他類型,像range)也可以獲取到一個step屬性,指定了值在增減過程固定改變的值(像向上增加和向下減去的按鈕)。

完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
body {
font: 1em sans-serif;
padding: 0;
margin : 0;
}

form {
max-width: 200px;
margin: 0;
padding: 0 5px;
}

p > label {
display: block;
}

input[type=text],
input[type=email],
input[type=number],
textarea,
fieldset {
/* required to properly style form
elements on WebKit based browsers */
-webkit-appearance: none;

width : 100%;
border: 1px solid #333;
margin: 0;

font-family: inherit;
font-size: 90%;

-moz-box-sizing: border-box;
box-sizing: border-box;
}

input:invalid {
box-shadow: 0 0 5px 1px red;
}

input:focus:invalid {
outline: none;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<form>
<p>
<fieldset>
<legend>Title<abbr title="此字段是必填字段">*</abbr></legend>
<input type="radio" required name="title" id="r1" value="Mr"><label for="r1">Mr.</label>
<input type="radio" required name="title" id="r2" value="Ms"><label for="r2">Ms.</label>
</fieldset>
</p>
<p>
<label for="n1">請問你幾歲了?</label>
<!-- pattern屬性可以作為瀏覽器的後備
         不要實現數字輸入類型,但支持pattern屬性。
         請注意,支持pattern屬性的瀏覽器將成為它
         與數字字段一起使用時無聲地失敗。
         它在這裡的用法僅作為後備 -->
<input type="number" min="12" max="120" step="1" id="n1" name="age"
pattern="\d+">
</p>
<p>
<label for="t1">請問你最喜歡的水果是什麼?<abbr title="此字段是必填字段">*</abbr></label>
<input type="text" id="t1" name="fruit" list="l1" required
pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range">
<datalist id="l1">
<option>Banana</option>
<option>Cherry</option>
<option>Apple</option>
<option>Strawberry</option>
<option>Lemon</option>
<option>Orange</option>
</datalist>
</p>
<p>
<label for="t2">請問你的電子郵件是什麼?</label>
<input type="email" id="t2" name="email">
</p>
<p>
<label for="t3">留言短信</label>
<textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
</p>
<p>
<button>提交</button>
</p>
</form>

自定義錯誤訊息

每次我們輸入無效的表單資料時, 瀏覽器總會顯示錯誤的訊息. 但是顯示的訊息取決於你所使用的瀏覽器。

這些自動拋出的錯誤有兩個缺點:

  • 沒有標準可以讓CSS 來改變給人們看到的樣子。
  • 這依賴於他們使用的瀏覽器環境, 意味著你使用不同的語言頁面得到的反饋訊息也是不同語言的。

自定義這些消息的外觀和文本,你必須使用JavaScript; 不能使用HTML 和CSS 來改變。

HTML5提供the constraint validation API來檢測自定義表單元素的狀態.除此之外,他可以改變錯誤的文本訊息。

1
2
3
4
5
<form>
<label for="mail">我希望你能提供電子信箱</label>
<input type="email" id="mail" name="mail" required>
<button>Submit</button>
</form>
1
2
3
4
5
6
7
8
9
var email = document.getElementById("mail");

email.addEventListener("input", function (event) {
if (email.validity.typeMismatch) {
email.setCustomValidity("我期待的是電子信箱,親愛的!");
} else {
email.setCustomValidity("");
}
});

到這邊基本的驗證就差不多了都實作了,下一章節將會進入到使用JavaScript校驗表單。

這邊轉載了大量的文章和內容,但是每個部分我都有實際去做過,並了解過。這邊轉載主要是做筆記記錄。

註:以上參考了
MDN web docsForm data validation 文章。
MDN web docsRegular Expressions 文章。
MDN web docsthe constraint validation API 文章。
forms-suck
Rubular