Like Share Discussion Bookmark Smile

J.J. Huang   2019-06-03   Web   瀏覽次數:

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

在上一章Web - 第二十二章 | HTML 表單資料驗證(一),有大致上介紹了HTML5的表單驗證,也有稍微使用JavaScript做替換內容;在這邊將會使用JavaScript進行表單的驗證,可以更靈活的去做驗證。

使用JavaScript校驗表單

如果你想控制原生錯誤訊息的外觀和感覺,或者你想處理不支持HTML內置表單驗證的瀏覽器,則必須使用Javascript。

HTML5用於校驗約束的API

現在越來越多的瀏覽器支持約束驗證API,並且它正在變得可靠。這些API 由一組方法和屬性組成,可在每個表單元素上調用。

用於校驗的API 及屬性:

屬性 描述
validationMessage 一個本地化消息,描述元素不滿足驗證條件時(如果有的話)的文本訊息。
如果元素無需驗證(willValidate 為 false),或元素的值滿足驗證條件時,為空字符串。
validity 一個ValidityState對象,描述元素的驗證狀態。
validity.customError 如果元素設置了自定義錯誤,返回 true;否則返回false。
validity.patternMismatch 如果元素的值不匹配所設置的正則表達式,返回 true,否則返回 false。
當此屬性為 true 時,元素將命中 :invalidCSS偽類。
validity.rangeOverflow 如果元素的值高於所設置的最大值,返回true,否則返回false。
當此屬性為 true 時,元素將命中 :invalidCSS偽類。
validity.rangeUnderflow 如果元素的值低於所設置的最小值,返回true,否則返回false。
當此屬性為 true 時,元素將命中 :invalidCSS偽類。
validity.stepMismatch 如果元素的值不符合step屬性的規則,返回true,否則返回false。
當此屬性為 true 時,元素將命中 :invalidCSS偽類。
validity.tooLong 如果元素的值超過所設置的最大長度,返回true,否則返回false。
當此屬性為 true 時,元素將命中 :invalidCSS偽類。
validity.typeMismatch 如果元素的值出現語法錯誤,返回true,否則返回false。
當此屬性為 true 時,元素將命中 :invalidCSS偽類。
validity.valid 如果元素的值不存在驗證問題,返回true,否則返回false。
當此屬性為 true 時,元素將命中 :validCSS偽類,否則命中:invalidCSS偽類。
validity.valueMissing 如果元素設置了required屬性且值為空,返回true,否則返回false。
當此屬性為true時,元素將命中 :invalidCSS偽類。
willValidate 如果元素在表單提交時將被驗證,返回true,否則返回false。

校驗約束API 的方法:
方法 描述
checkValidity() 如果元素的值不存在驗證問題,返回true,否則返回false。如果元素驗證失敗,此方法會觸發invalid事件。
setCustomValidity(message) 為元素添加一個自定義的錯誤消息;如果設置了自定義錯誤消息,則該元素被認為是無效的,並顯示指定的錯誤。這允許你使用JavaScript代碼來建立驗證失敗,而不是用標準約束驗證API所提供的。在報告問題時,將向用戶顯示自定義消息。
如果參數為空,則清空自定義錯誤。

註:對於舊版瀏覽器,可以使用polyfill(例如Hyperform,來彌補其對約束驗證API支持的不足。既然你已經使用JavaScript,在你的網站或Web應用程序的設計和實現中使用polyfill並不是累贅。

使用校驗約束API 的例子

1
2
3
4
5
6
7
8
9
10
<form novalidate>
<p>
<label for="mail">
<span>請輸入電子郵件信箱</span>
<input type="email" id="mail" name="mail">
<span class="error" aria-live="polite"></span>
</label>
</p>
<button>Submit</button>
</form>

這個簡單的表單使用novalidate屬性關閉瀏覽器的自動驗證;這允許我們使用腳本控製表單驗證。但是,這並不禁止支持約束驗證API的應用和以下CSS偽類::valid:invalid:in-range:out-of-range。這意味著,即使瀏覽器在發送資料之前沒有自動檢查表單的有效性,你仍然可以自己做,並相應地設置表單的樣式。

aria-live屬性確保我們的自定義錯誤訊息將呈現給所有人,包括使用屏幕閱讀器等輔助技術的人。

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
44
45
46
47
48
49
50
51
52
53
54
55
body {
font: 1em sans-serif;
padding: 0;
margin : 0;
}

form {
max-width: 200px;
}

p * {
display: block;
}

input[type=email]{
-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{
border-color: #900;
background-color: #FDD;
}

input:focus:invalid {
outline: none;
}

/* 錯誤消息的樣式 */
.error {
width : 100%;
padding: 0;

font-size: 80%;
color: white;
background-color: #900;
border-radius: 0 0 5px 5px;

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

.error.active {
padding: 0.3em;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var form  = document.getElementsByTagName('form')[0];
var email = document.getElementById('mail');
var error = document.querySelector('.error');

email.addEventListener("input", function (event) {
// 當用戶輸入訊息時,驗證 email 字段
if (email.validity.valid) {
// 如果驗證通過,清除已顯示的錯誤消息
error.innerHTML = ""; // 重置消息的内容
error.className = "error"; // 重置消息的顯示狀態
}
}, false);
form.addEventListener("submit", function (event) {
// 當用戶提交表單時,驗證 email 字段
if (!email.validity.valid) {

// 如果驗證失敗,顯示一個自定義錯誤
error.innerHTML = "I expect an e-mail, darling!";
error.className = "error active";
// 還需要阻止表單提交事件,以取消資料傳送
event.preventDefault();
}
}, false);

約束驗證API 為你提供了一個強大的工具來處理表單驗證,讓你可以對用戶界面進行極大的控制,而不僅僅是僅使用HTML 和CSS。

不使用內建API時的表單驗證

有時,例如使用舊版瀏覽器或自定義小部件,你將無法(或不希望)使用約束驗證API。在這種情況下,你仍然可以使用JavaScript來驗證你的表單。驗證表單比真實資料驗證更像是一個用戶界面問題。

要驗證表單,你必須問自己幾個問題:

  • 我應該進行什麼樣的驗證?
    • 你需要確定如何驗證你的資料:字符串操作,類型轉換,正則表達式等。這取決於你。只要記住,表單資料總是文本,並總是以字符串形式提供給腳本。
  • 如果表單驗證失敗,我該怎麼辦?
    • 這顯然是一個UI 的問題。你必須決定表單的行為方式:表單是否發送資料?是否突出顯示錯誤的字段?是否顯示錯誤消息?
  • 如何幫助用戶糾正無效資料?

不使用校驗約束API 的例子

1
2
3
4
5
6
7
8
9
10
11
12
<form> 
<p>
<label for="mail">
<span>請輸入電子郵件信箱</span>
<input type="email" id="mail" name="mail">
<span class="error" aria-live="polite"></span>
</label>
</p>
<!-- 某些舊版瀏覽器需要具有`type`屬性
    在`button`element - >上顯式設置為`submit` -->
<button type="submit">Submit</button>
</form>

註:HTML幾乎是一樣的;我們只是關閉了HTML驗證功能。請注意,ARIA是與HTML5無關的獨立規範。

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
44
45
46
47
48
49
50
51
52
53
54
55
body {
font: 1em sans-serif;
padding: 0;
margin : 0;
}

form {
max-width: 200px;
}

p * {
display: block;
}

input.mail {
-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{
border-color: #900;
background-color: #FDD;
}

input:focus.invalid {
outline: none;
}

/* 錯誤消息的樣式 */
.error {
width : 100%;
padding: 0;

font-size: 80%;
color: white;
background-color: #900;
border-radius: 0 0 5px 5px;

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

.error.active {
padding: 0.3em;
}

註:CSS也不需要太多的改動,我們只需將:invalid偽類變成真實的類,並避免使用不適用於Internet Explorer 6的屬性選擇器。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
var form  = document.getElementsByTagName('form')[0];
var email = document.getElementById('mail');

// 以下是在 DOM 中訪問下一個兄弟元素的技巧
// 這比較危險,很容易引起無限循環
// 在現代瀏覽器中,應該使用 element.nextElementSibling
var error = email;
while ((error = error.nextSibling).nodeType != 1);

// 按照 HTML5 規範
var emailRegExp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;

// 許多舊版瀏覽器不支持 addEventListener 方法
// 這只是其中一種簡單的處理方法
function addEvent(element, event, callback) {
var previousEventCallBack = element["on"+event];
element["on"+event] = function (e) {
var output = callback(e);

// 返回 `false` 来停止回調,並中斷事件的執行
if (output === false) return false;

if (typeof previousEventCallBack === 'function') {
output = previousEventCallBack(e);
if(output === false) return false;
}
}
};

// 現在我們可以重構字段的驗證約束了
// 由於不使用 CSS 偽類, 我們必須明確地設置 valid 或 invalid 類到 email 字段上
addEvent(window, "load", function () {
// 在這裡驗證字段是否為空(請記住,該字段不是必需的)
  // 如果非空,檢查它的內容格式是不是合格的 e-mail 地址
var test = email.value.length === 0 || emailRegExp.test(email.value);

email.className = test ? "valid" : "invalid";
});

// 處理用戶輸入事件
addEvent(email, "input", function () {
var test = email.value.length === 0 || emailRegExp.test(email.value);
if (test) {
email.className = "valid";
error.innerHTML = "";
error.className = "error";
} else {
email.className = "invalid";
}
});

// 處理表單提交事件
addEvent(form, "submit", function () {
var test = email.value.length === 0 || emailRegExp.test(email.value);

if (!test) {
email.className = "invalid";
error.innerHTML = "I expect an e-mail, darling!";
error.className = "error active";

// 某些舊版瀏覽器不支持 event.preventDefault() 方法
return false;
} else {
email.className = "valid";
error.innerHTML = "";
error.className = "error";
}
});

建立自己的驗證系統並不難。困難的部分是使其足夠通用,以跨平台和任何形式使用它可以建立。有許多庫可用於執行表單驗證; 你應該毫不猶豫地使用它們。

遠程校驗

在某些情況下,執行一些遠程驗證可能很有用。當用戶輸入的資料與存儲在應用程序伺服器端的附加資料綁定時,這種驗證是必要的。一個用例就是註冊表單,在這裡你需要一個用戶名。為了避免重複,執行一個AJAX 請求來檢查用戶名的可用性,而不是要求用戶發送資料,然後發送帶有錯誤的表單。

執行這樣的驗證需要採取一些預防措施:

  • 它要求公開API 和一些資料;你需要確保它不是敏感資料。
  • 網路滯後需要執行異步驗證。這需要一些用戶界面的工作,以確保如果驗證不正確執行,用戶不會被阻止。

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

註:以上參考了
MDN web docsForm data validation 文章。
Form-Field Validation: The Errors-Only Approach
Web Form Validation: Best Practices and Tutorials
Best Practices for Hints and Validation in Web Forms
Inline Validation in Web Forms
Validate.js
Validation
Hyperform