自學日記

學習程式相關知識及學習法

0%

作品練習 Form Validator 表單驗證

作品練習Form Validator 表單驗證

本練習作品學習要點為:理解visiblity: hiddenvisiblity: visible差別,以及學習如何有簡(if else)至深(forEach),重構表單並增加表單條件及email輸入判斷

HTML

本節摘要重點:

  • 建立架構,並為各標籤命名。
  • 命名ID方便於JS中做設定應用

    完整html:

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Form Validator</title>

</head>
<body>
<div class="container">
<!-- 於javascript中取其id -->
<form id="form" class="form">
<h2>Register With Us</h2>
<div class="form-control ">
<label for="username">username</label>
<input type="text" id="username" placeholder="Enter username">
<small>Error message</small>
</div>
<div class="form-control ">
<label for="email">Email</label>
<input type="text" id="email" placeholder="Enter email">
<small>Error message</small>
</div>
<div class="form-control">
<label for="password">Password</label>
<input type="password" id="password" placeholder="Enter password">
<small>Error message</small>
</div>
<div class="form-control">
<label for="password2">Confirm Password</label>
<input type="password" id="password2" placeholder="Enter password again">
<small>Error message</small>
</div>
<button>Submit</button>
</form>
</div>
<script src="script.js"></script>
</body>
</html>

HTML結果呈現

CSS

本節摘要筆記重點:(重點於下方有詳細資訊)

  • 設定字型
  • 根目錄選取器
  • visibility:hidden VS display:none

    完整css

    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
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap');

    :root{
    --success-color:#2ecc71;
    --error-color:#e74c3c;
    }

    *{
    box-sizing: border-box;
    }
    /*這邊設置flex讓內容置中 另外設置margin 0取消預設值*/
    body{
    background-color: #f9fafb;
    font-family: 'Open Sans',sans-serif;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    margin: 0;
    }

    .container{
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
    width: 400px;
    }

    h2{
    text-align: center;
    margin: 0 0 20px;
    }

    .form{
    padding: 30px 40px;
    }

    .form-control{
    margin-bottom: 10px;
    padding-bottom: 20px;
    position: relative;
    }

    .form-control label{
    color: #777;
    display: block;
    margin-bottom: 5px;
    }

    .form-control input{
    border: 2px solid #f0f0f0;
    border-radius: 4px;
    display: block;
    width: 100%;
    padding: 10px;
    font-size: 14px;
    }

    .form-control input:focus{
    outline: 0;
    border-color: #777;
    }

    .form-control.success input{
    border-color: var(--success-color);
    }

    .form-control.error input{
    border-color: var(--error-color);
    }

    .form-control small{
    color: var(--error-color);
    position: absolute;
    bottom: 0;
    left: 0;
    visibility: hidden;
    }

    .form-control.form-control.error small{
    visibility: visible;
    }

    .form button{
    cursor: pointer;
    background-color: #3498db;
    border: 2px solid #3498db;
    border-radius: 4px;
    color: #fff;
    display: block;
    font-size: 16px;
    padding: 10px;
    margin-top: 20px;
    width: 100%;
    }

    設定字型

    1
    2
    3
    /*設定字型,可以在html<head>之間輸入<link href="https://..."*/
    /*或者在css樣式<style>輸入@import加入字型來源*/
    @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap');

    根目錄選取器

1
2
3
4
5
6
7
8
9
/*根目錄選取器,應用多是搭配 CSS 變數 ( CSS variables ) 來應用*/
:root{
--success-color:#2ecc71;
--error-color:#e74c3c;
}

*{
box-sizing: border-box;
}
  • :root 通常搭配css變數使用,因過去瀏覽器支援度限制,比較少推廣,主要為建立變數與套用變數。

建立變數:

1
2
3
:root {
--A-B: #f00;
}

套用變數:

1
2
3
.section-item {
color: var( --A-B );
}

以:root為關鍵符號,–建立CSS變數,賦予該名A-B,然後再賦值。

可參考:

  • :root 根目錄選取器 - 叫你阿爸出來講

    visibility:hidden VS display:none

    visibility:hidden,物件是確實的被隱藏的,但物件的位置仍舊保持著不會消失,使用display: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
    .form-control input:focus{
    outline: 0;
    border-color: #777;
    }

    .form-control.success input{
    border-color: var(--success-color);
    }

    .form-control.error input{
    border-color: var(--error-color);
    }

    .form-control small{
    color: var(--error-color);
    position: absolute;
    bottom: 0;
    left: 0;
    /*visibility:hidden VS */
    /*使用visibility:hidden,物件是確實的被隱藏的,但物件的位置仍舊保持著不會消失 */
    /*使用display:none,物件連原本所在的位置都一起被隱藏了,為完全消失*/
    visibility: hidden;
    }

    .form-control.form-control.error small{
    visibility: visible;

Javascript

本節摘要重點:
先使用較直觀式的if else判斷form是否有填寫內容,之後再重構讓程式碼更加簡潔:

利用getElementById 來取得html中表單中的值(ID)

利用const 宣告(優先使用const),及使用getElementById取得html表單元素內容的值。

“const ”— 一般使用在識別值(identifier)不會被重新指定值。
“let”— 一般使用在變數(variable)可能會被重新指定值。

1
2
3
4
const form = document.getElementById('form');
const username = document.getElementById('username');
const password = document.getElementById('password');
const password2 = document.getElementById('password2');

可參考:

  • 語言基礎 04 -基本函式(Function)概念-let、const的暫時性死區
  • 宣告 JavaScript: var, let, const 差異
  • 用 JavaScript 來取得表單元素內容的值(取值)

    設定監聽事件

    針對form設定監聽事件並利用if else判斷輸入欄內是否為空,有內容顯示成功的function,反之亦然
    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
    // 事件監聽(Event listeners)
    form.addEventListener('submit', function (e) {

    //阻擋點選submit 按鈕,防止默認行為並避免表單提交以重新加載頁面的方法
    e.preventDefault();


    //判斷輸入內容是否為空,空的話顯示使用showError函式,
    //否則呈現使用者輸入的內容
    if (username.value === '') {
    showError(username, 'Username is required');
    } else {
    showSuccess(username);

    }

    //email多了一個內容驗證,
    //假若email內容沒有通過isValidation驗證,
    //則顯示'Email is not valid',如果內容為空則跑showError函式
    if (email.value === '') {
    showError(email, 'Email is required');
    } else if (!isValidEmail(email.value)) {
    showError(email, 'Email is not valid');
    } else {
    showSuccess(email);
    }
    if (password.value === '') {
    showError(password, 'password is required');
    } else {
    showSuccess(password);

    }
    if (password2.value === '') {
    showError(password2, 'Password2 is required');
    } else {
    showSuccess(password2);
    }

    設定輸入成功與否function的效果

    error function顯示紅字及紅框(CSS有設定),success function顯示綠框。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function showError(input, message) {
    const formControl = input.parentElement;
    formControl.className = 'form-control error';
    const small = formControl.querySelector('small');
    small.innerText = message;
    }

    function showSuccess(input) {
    const formControl = input.parentElement;
    formControl.className = 'form-control success';
    }

使用正則表達是去判斷email驗證部分

1
2
3
4
5
6
7
8
    function isValidEmail(email) {

const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());

};
}

重構原始碼,減少if else重複,並讓其更簡潔

由於,有些部分前面有講解,此處將重構後原始code碼補上,利用註解解釋:

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
const form = document.getElementById('form');
const username = document.getElementById('username');
const email = document.getElementById('email');
const password = document.getElementById('password');
const password2 = document.getElementById('password2');

// Show input error message
function showError(input, message) {
const formControl = input.parentElement;
formControl.className = 'form-control error';
const small = formControl.querySelector('small');
small.innerText = message;
}

// Show success outline
function showSuccess(input) {
const formControl = input.parentElement;
formControl.className = 'form-control success';
}

// Check email is valid
function checkEmail(input) {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
//trim 的用法:會將前後的空白字元,包括行的結束字符從字串中去除。
if (re.test(input.value.trim())) {
showSuccess(input);
} else {
showError(input, 'Email is not valid');
}
}

//Check required fields
function checkRequired(inputArr) {
inputArr.forEach(function (input) {
if (input.value.trim() === '') {
console.log(input.id);
showError(input, `${getFieldName(input)} is required`);
} else {
showSuccess(input);
}
})
}

//確認輸入字元長度(check input length)
//蠻直觀的值直接做比大小的動作,一樣搭配Es6的方式做字串串接
function checkLength(input, min, max) {
if (input.value.length < min) {
showError(input, `${getFieldName(input)} must be at least ${min} characters`)
} else if (input.value.length > max) {
showError(input, `${getFieldName(input)} must be less than${max} characters`)
} else {
showSuccess(input);
}
}

//確認兩個密碼是否吻合如果不合則顯示錯誤在password2(check passwords match)
function checkPasswordMatch(input1, input2) {
if (input1.value !== input2.value) {
showError(input2, "Password do not match");
// console.log("if");
} else {
showSuccess(input2);
// console.log("else");
}
}

function getFieldName(input) {
return input.id.charAt(0).toUpperCase() + input.id.slice(1);
}

// Event listeners
//把功能的名稱改的整齊一些
form.addEventListener('submit', function (e) {
e.preventDefault();
checkRequired([username, email, password, password2]);

//注意length的參數必須有起始值以及終點值
checkLength(username, 3, 15);
checkLength(password, 6, 25);
checkEmail(email);
//這邊要輸入兩個要比較的參數
checkPasswordMatch(password, password2);
});

可參考: