Этот пост из моего блога.
Вы также можете проверить видео на YouTube.
Создать html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Form Validation</title> <link rel="stylesheet" href="style.css"> </head> <body> <header> <h1>Form Validation</h1> </header>
<div class="container"> <form class="form" data-form> <div class="form__group" data-formgroup> <label for="name">Full name</label> <input type="text" id="name" name="name" data-validate data-required data-required-message="Name is required" > <div class="form__error" data-formerror></div> </div>
<div class="form__group" data-formgroup> <label for="email">Email</label> <input type="text" name="email" id="email" data-validate data-required data-email data-email-message="Email is not valid" > <div class="form__error" data-formerror></div> </div>
<div class="form__group" data-formgroup> <label for="password">Password</label> <input type="password" name="password" id="password" data-validate data-required data-minlength="6" data-maxlength="16" data-match-with="confirmPassword" > <div class="form__error" data-formerror></div> </div>
<div class="form__group" data-formgroup> <label for="confirmPassword">Confirm Password</label> <input type="password" name="confirmPassword" id="confirmPassword" data-validate data-required data-match="password" data-match-message="Passwords must be equal" > <div class="form__error" data-formerror></div> </div>
<div class="form__group form__group--radio" data-formgroup> <p>Gender</p> <label> <input type="radio" name="gender" value="female" data-validate data-error-message="Gender is required"> <span>Female</span> </label> <label> <input type="radio" name="gender" value="male" data-validate> <span>Male</span> </label> <div class="form__error" data-formerror></div> </div>
<div class="form__group" data-formgroup> <label for="difficulty">Difficulty</label> <select name="difficulty" id="difficulty" data-validate data-required> <option value="">Select difficulty</option> <option value="easy">Easy</option> <option value="medium">Medium</option> <option value="hard">Hard</option> </select> <div class="form__error" data-formerror></div> </div>
<div class="form__group" data-formgroup> <label for="image">Image</label> <input type="file" name="image" id="image" data-validate data-required data-maxfilesize="1024" data-allowed-types="jpg,png" data-allowed-types-message="Only jpg and png formats allowed" > <div class="form__error" data-formerror></div> </div>
<div class="form__group"> <label for="description">Description</label> <textarea name="description" id="description"></textarea> </div>
<div class="form__group form__group--checkbox" data-formgroup> <label> <input type="checkbox" name="terms" id="terms" data-validate data-error-message="You must accept our Terms and Conditions" > <span>Terms and Conditions</span> </label> <div class="form__error" data-formerror></div> </div>
<button type="submit" class="btn">Submit</button> </form> </div>
<script src="form.js"></script> </body> </html>
Для проверки формы добавьте атрибут data-form к элементу формы. Затем добавьте data-formgroup в каждый div-оболочку. Каждый div имеет элемент метки, элемент ввода и div для сообщения об ошибке.
На этом этапе ничего не будет проверено, потому что нам нужно добавить еще несколько атрибутов к элементу ввода. Первый — проверка данных, он указывает javascript включить этот элемент в процесс проверки. Теперь нам нужно добавить атрибуты, чтобы сообщить js, какую проверку ему нужно проверить.
данные-требуются, данные-требуются-сообщение
Этот сообщит js, что требуется входное значение. Каждый атрибут также имеет соответствующий атрибут для сообщения об ошибке. Чтобы добавить его, просто добавьте -message к атрибуту имени атрибута. Например, для обязательных данных мы можем добавить data-required-message="This field is required". Если не будет добавлено сообщение об ошибке по умолчанию, оно будет добавлено в javascript.
минимальная длина данных, минимальная длина данных-сообщение
Это используется, чтобы сообщить js, что длина входного значения не может быть меньше числа, которое вы указали в этом атрибуте, например, data-minlength="5". Для сообщения об ошибке используйте data-minlength-message. Опять же, этот атрибут является необязательным, если он не установлен, будет использоваться сообщение об ошибке по умолчанию.
данные-maxlength, данные-maxlength-сообщение
Этот проверяет, превышает ли длина входного значения число, которое вы указали в атрибуте.
электронная почта с данными, электронная почта с данными
data-email проверяет, является ли введенное значение действительным адресом электронной почты.
совпадение данных, совпадение данных с, сообщение-соответствие данных
Эти атрибуты используются в паролях. Например, когда у вас есть регистрационная форма, вы, вероятно, добавите поле подтверждения пароля, чтобы пользователь мог повторить пароль.
В поле исходного пароля вы можете добавить атрибут data-match-with, и значение этого атрибута должно быть значением атрибута имени ввода подтверждения пароля. И при вводе подтверждения пароля вы должны добавить атрибут match-with со значением атрибута имени исходного ввода пароля. А для сообщения об ошибке вы можете добавить ошибку совпадения данных к вводу подтверждения пароля.
Радио-кнопки
Если вы хотите проверять переключатели, вы должны добавить проверку данных к каждому переключателю в группе, а для сообщения об ошибке добавить сообщение об ошибке данных к первому переключателю в группе.
флажок
Чтобы проверить флажок, добавьте атрибут проверки данных в флажок, а для сообщения об ошибке вы можете добавить сообщение об ошибке данных.
ПРИМЕЧАНИЕ: для переключателей и флажков вам не нужно добавлять атрибут data-required.
ввод файла, data-maxfilesize, data-allowed-types
Для входных файлов вы можете добавить атрибут data-maxfilesize, он будет проверять, больше ли размер файла, чем значение этого атрибута, и в этом он покажет ошибку. Для ошибки вы можете использовать data-maxfilesize-message.
И если вы хотите проверить тип файла, вы можете добавить атрибут data-allowed-types. Значением этого атрибута является расширение файла. Чтобы добавить несколько значений, разделите их запятой и без пробела после запятой. Например: data-allowed-types="jpg,png,pdf". Для сообщения об ошибке используйте атрибут data-allowed-types-message.
И это все, что касается атрибутов. Еще одна вещь: каждый блок ошибок должен иметь атрибут data-formerror.
Создать CSS
@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
:root { --primary-color: #009e6c; --error-color: #e70f0f; }
* { box-sizing: border-box; margin: 0; }
body { font-family: 'Roboto', sans-serif; font-size: 16px; line-height: 1.5; }
header { background-color: var(--primary-color); color: #fff; text-align: center; padding: 50px 0; margin-bottom: 50px; }
.container { max-width: 600px; margin: 0 auto; padding-bottom: 50px; }
.form { border: 1px solid #eee; padding: 40px; }
.form__group { margin-bottom: 20px; }
.form__group label, .form__group p { display: block; font-size: 14px; margin-bottom: 5px; }
.form__group--radio label, .form__group--checkbox label { display: inline-flex; align-items: center; margin-right: 15px; }
.form__group--radio label span, .form__group--checkbox label span { margin-left: 5px; }
.form__group input[type="text"], .form__group input[type="file"], .form__group input[type="password"], .form__group select, .form__group textarea { display: block; width: 100%; padding: 10px; font-size: 14px; border: 1px solid #eee; outline: 0; transition: box-shadow .3s ease; }
.form__group input[type="text"]:focus, .form__group input[type="file"]:focus, .form__group input[type="password"]:focus, .form__group select:focus, .form__group textarea:focus { box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }
.form__group input[type="text"].error, .form__group input[type="file"].error, .form__group input[type="password"].error, .form__group select.error, .form__group textarea.error { border-color: var(--error-color); }
.form__error { color: var(--error-color); font-size: 12px; padding: 5px 0; display: none; }
.btn { display: inline-flex; align-items: center; padding: 10px 20px; background-color: var(--primary-color); color: #fff; border: 0; outline: 0; cursor: pointer; }
CSS очень прост. Я только что добавил несколько стилей для стилизации формы. Самое главное — скрыть ошибочный div по умолчанию и добавить некоторый класс к элементу ввода, когда ошибка видна, в данном примере это класс ошибки.
JS-часть
Первое, что мы должны сделать в js, — это получить все элементы формы с атрибутом data-form, проверить, существуют ли они на странице, а затем прокрутить их.
// Get elements const forms = document.querySelectorAll('form[data-form]');
// Check if form elements exist if(forms.length > 0) { // Loop through forms for(let form of forms) { // Get all inputs with data-validate attribute const inputs = form.querySelectorAll('[data-validate]');
// Submit form form.addEventListener('submit', submitForm.bind(form, inputs)); // Loop through inputs inputs.forEach(input => { // Add input event to all inputs and listen to inputChange function input.addEventListener('input', inputChange); }); } }
После этого для каждой формы мы можем получить элементы формы, которые необходимо проверить, мы можем получить их по атрибуту [data-validate]. Затем мы можем добавить прослушиватель событий для события ввода, чтобы проверять каждый ввод при изменении значения ввода. А также добавьте прослушиватель событий для типа события отправки в форме, чтобы при нажатии кнопки отправки мы также могли проверять входные данные.
Теперь нужные нам функции:
// Input change function inputChange() { const input = this; validateInput(input); }
// Validate input function validateInput(input) { // Get the value and error element const value = input.value; const errorEl = input.closest('[data-formgroup]').querySelector('[data-formerror]'); // Declare error variable and assign null by default let error = null;
// Check in input has data-required attribute and if the value is empty, and if the input is not radio or checkbox if((input.type !== 'radio' || input.type !== 'checkbox') && input.dataset.required !== undefined && value === '') { error = input.dataset.requiredMessage ? input.dataset.requiredMessage : 'This field is required'; input.classList.add('error'); }
// Check if input is checkbox and it is not checked if(input.type === 'checkbox' && !input.checked) { error = input.dataset.errorMessage ? input.dataset.errorMessage : 'This field is required'; }
// Check if input is radio if(input.type === 'radio') { // Get all radio inputs in a group const radios = input.closest('[data-formgroup]').querySelectorAll('input[type="radio"]'); let isChecked = false; let errorMsg = ''; // Loop through radios and check if any radio is checked and if it is checked set isChecked to true radios.forEach(radio => { if(radio.checked) { isChecked = true; } if(radio.dataset.errorMessage) { errorMsg = input.dataset.errorMessage; } }); if(!isChecked) { error = errorMsg !== '' ? errorMsg : 'This field is required'; } }
// Check if input has data-minlength attribute and if value length is smaller than this attribute, if so show the error if(!error && input.dataset.minlength !== undefined && value.length < +input.dataset.minlength) { error = input.dataset.minlengthMessage ? input.dataset.minlengthMessage : `Please enter at least ${input.dataset.minlength} characters`; input.classList.add('error'); }
// Check if input has data-maxlength attribute and if value length is greater than this attribute, if so show the error if(!error && input.dataset.maxlength !== undefined && value.length > +input.dataset.maxlength) { error = input.dataset.maxlengthMessage ? input.dataset.maxlengthMessage : `Only ${input.dataset.maxlength} characters allowed`; input.classList.add('error'); }
// Check if input has data-email attribute and if email is not valid if(!error && input.dataset.email !== undefined && !validateEmail(value)) { error = input.dataset.emailMessage ? input.dataset.emailMessage : 'Invalid email address'; input.classList.add('error'); }
// Check if input has data-match attribute and if value is not equal to the value of the element with name attribute equal to this data-match attribute if(!error && input.dataset.match !== undefined && value !== input.closest('[data-form]').querySelector(`[name="${input.dataset.match}"]`).value) { error = input.dataset.matchMessage ? input.dataset.matchMessage : 'Fields are not the same'; input.classList.add('error'); }
// Check if input has data-match-with attribute if(input.dataset.matchWith !== undefined) { // Get the input that has a name attribute equal to value of data-match-with attribute const inputToMatch = input.closest('[data-form]').querySelector(`[name="${input.dataset.matchWith}"]`); // Get the error element of that input const inputToMatchError = inputToMatch.closest('[data-formgroup]').querySelector('[data-formerror]'); // If values are equal remove error class from input and hide error element if(value === inputToMatch.value) { inputToMatch.classList.remove('error'); inputToMatchError.style.display = 'none'; }else { // Add error class to input and show error element inputToMatch.classList.add('error'); inputToMatchError.style.display = 'block'; inputToMatchError.innerText = inputToMatch.dataset.matchMessage || 'Fields are not the same'; } }
// Check if input is file input and if has data-maxfilesize attribute and if file size is greater than the value of this data-maxfilesize attribute if(!error && input.type === 'file' && input.dataset.maxfilesize !== undefined && input.files[0].size > +input.dataset.maxfilesize * 1024) { error = input.dataset.maxfilesizeMessage ? input.dataset.maxfilesizeMessage : 'File is too large'; input.classList.add('error'); }
// Check if input is file input and if it has data-allowed-types attribute and if file type is not equal to one of the values in data-allowed-type attribute if(!error && input.type === 'file' && input.dataset.allowedTypes !== undefined && !input.dataset.allowedTypes.includes(input.files[0].type.split('/')[1])) { error = input.dataset.allowedTypesMessage ? input.dataset.allowedTypesMessage : 'Invalid file type'; input.classList.add('error'); } // If there is no error remove error class from the input, remove message from error element and hide it if(!error) { input.classList.remove('error'); errorEl.innerText = ''; errorEl.style.display = 'none'; } else { // If there is error set error message and show error element errorEl.innerText = error; errorEl.style.display = 'block'; }
return error; }
// Submit form - on submit btn click function submitForm(inputs, e) { e.preventDefault(); const errors = []; inputs.forEach(input => { const error = validateInput(input); if(error) { errors.push(error); } });
if(errors.length === 0) { console.log('form can be submitted...'); } }
// Validate email function validateEmail(email) { var 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()); }
Прежде всего, нам нужна функция inputChange. В этой функции мы получаем ввод по этому ключевому слову, а затем мы можем просто вызвать функцию validateInput и передать ввод в качестве аргумента.
Для submitForm мы делаем то же самое, за исключением того, что мы передаем все входные данные с помощью bind для циклического перебора входных данных и вызываем validateInput для каждого входного элемента. Мы также можем создать массив ошибок, и если validateInput возвращает ошибку, а не ноль, мы добавляем эту ошибку в массив. Таким образом, после forEach мы можем проверить, пуст ли массив ошибок, и только в этом случае мы можем отправить форму.
Теперь самая важная функция, validateInput.
Сначала мы получаем значение ввода. А затем элемент error div и установите для переменной error значение null по умолчанию.
// Get the value and error element
const value = input.value;
const errorEl = input.closest('[data-formgroup]').querySelector('[data-formerror]');
// Declare error variable and assign null by default
let error = null;
В первом операторе if мы проверяем, не является ли тип ввода радио или флажком, имеет ли ввод атрибут data-required и является ли он пустым. В этом случае мы устанавливаем сообщение об ошибке, которое было передано в атрибут data-required-message, или мы используем сообщение об ошибке по умолчанию, если на входе нет атрибута data-required-message, и мы добавляем к входу класс ошибки.
// Check in input has data-required attribute and if the value is empty, and if the input is not radio or checkbox
if((input.type !== 'radio' || input.type !== 'checkbox') && input.dataset.required !== undefined && value === '') {
error = input.dataset.requiredMessage ? input.dataset.requiredMessage : 'This field is required';
input.classList.add('error');
}
Во втором операторе if мы проверяем, является ли тип ввода флажком и не установлен ли флажок. Если это правда, мы устанавливаем переменную ошибки в значение атрибута data-error-message или используем сообщение об ошибке по умолчанию.
// Check if input is checkbox and it is not checked
if(input.type === 'checkbox' && !input.checked) {
error = input.dataset.errorMessage ? input.dataset.errorMessage : 'This field is required';
}
В следующем операторе if мы проверяем, является ли тип ввода радио. Если это так, мы получаем все переключатели в этой группе, устанавливаем для переменной isChecked значение false, а для errorMsg — пустую строку.
Затем мы перебираем переключатели и устанавливаем для isChecked значение true, если радио проверено, и устанавливаем для errorMsg сообщение об ошибке (только если у радио есть атрибут data-error-message). После forEach мы проверяем, является ли isChecked ложным, поэтому, если ни один из переключателей не отмечен, мы устанавливаем ошибку в errorMsg или сообщение по умолчанию.
// Check if input is radio
if(input.type === 'radio') {
// Get all radio inputs in a group
const radios = input.closest('[data-formgroup]').querySelectorAll('input[type="radio"]');
let isChecked = false;
let errorMsg = '';
// Loop through radios and check if any radio is checked and if it is checked set isChecked to true
radios.forEach(radio => {
if(radio.checked) {
isChecked = true;
}
if(radio.dataset.errorMessage) {
errorMsg = input.dataset.errorMessage;
}
});
if(!isChecked) {
error = errorMsg !== '' ? errorMsg : 'This field is required';
}
}
ПРИМЕЧАНИЕ: в следующих операторах if мы всегда сначала проверяем, не установлена ли ошибка, потому что если это так, мы не хотим показывать несколько сообщений об ошибках.
Следующий оператор if предназначен для атрибута data-minlength. Здесь мы проверяем, добавлен ли к входным данным атрибут data-minlength и меньше ли длина входного значения, чем значение атрибута data-minlength. В этом случае показать сообщение об ошибке и добавить класс ошибки на вход.
// Check if input has data-minlength attribute and if value length is smaller than this attribute, if so show the error
if(!error && input.dataset.minlength !== undefined && value.length < +input.dataset.minlength) {
error =
input.dataset.minlengthMessage ? input.dataset.minlengthMessage : `Please enter at least ${input.dataset.minlength} characters`;
input.classList.add('error');
}
Следующий — для атрибута data-maxlength. Если этот атрибут находится на входе и если длина значения больше, чем значение этого атрибута, нам нужно показать сообщение об ошибке.
// Check if input has data-maxlength attribute and if value length is greater than this attribute, if so show the error
if(!error && input.dataset.maxlength !== undefined && value.length > +input.dataset.maxlength) {
error =
input.dataset.maxlengthMessage ? input.dataset.maxlengthMessage : `Only ${input.dataset.maxlength} characters allowed`;
input.classList.add('error');
}
Следующий — электронная почта данных. Этот проверяет, является ли электронная почта действительной, и если не отображается сообщение об ошибке. Я использую здесь функцию validateEmail, чтобы проверить ее правильность.
// Check if input has data-email attribute and if email is not valid
if(!error && input.dataset.email !== undefined && !validateEmail(value)) {
error =
input.dataset.emailMessage ? input.dataset.emailMessage : 'Invalid email address';
input.classList.add('error');
}
Теперь давайте проверим пароли. Сначала мы проверяем, имеет ли ввод атрибут соответствия данных и не равно ли входное значение значению ввода с атрибутом имени, который имеет то же значение, что и атрибут соответствия данных. В этом случае мы знаем, что они не равны, и можем показать сообщение об ошибке.
// Check if input has data-match attribute and if value is not equal to the value of the element with name attribute equal to this data-match attribute
if(!error && input.dataset.match !== undefined && value !== input.closest('[data-form]').querySelector(`[name="${input.dataset.match}"]`).value) {
error =
input.dataset.matchMessage ? input.dataset.matchMessage : 'Fields are not the same';
input.classList.add('error');
}
Выше оператор if предназначен для поля подтверждения пароля. И в исходное поле пароля мы должны добавить атрибут data-match-with. Теперь мы можем проверить, установлен ли атрибут data-match-with, и если это так, мы сначала получаем ввод со значением атрибута name, равным значению атрибута data-match-with, а также получаем блок ошибок этого ввода. Теперь мы можем проверить, равны ли значения этих входов, если они равны, мы можем удалить сообщение об ошибке и класс ошибки, иначе мы добавим сообщение об ошибке и класс ошибки в inputToMatch.
// Check if input has data-match-with attribute
if(input.dataset.matchWith !== undefined) {
// Get the input that has a name attribute equal to value of data-match-with attribute
const inputToMatch = input.closest('[data-form]').querySelector(`[name="${input.dataset.matchWith}"]`);
// Get the error element of that input
const inputToMatchError = inputToMatch.closest('[data-formgroup]').querySelector('[data-formerror]');
// If values are equal remove error class from input and hide error element
if(value === inputToMatch.value) {
inputToMatch.classList.remove('error');
inputToMatchError.style.display = 'none';
}else { // Add error class to input and show error element
inputToMatch.classList.add('error');
inputToMatchError.style.display = 'block';
inputToMatchError.innerText = inputToMatch.dataset.matchMessage || 'Fields are not the same';
}
}
В следующем операторе if мы проверяем, является ли тип ввода файлом, и имеет ли он атрибут data-maxfilesize и если размер выбранного файла больше, чем значение атрибута data-maxfilesize, в этом случае мы показываем ошибку.
// Check if input is file input and if has data-maxfilesize attribute and if file size is greater than the value of this data-maxfilesize attribute
if(!error && input.type === 'file' && input.dataset.maxfilesize !== undefined && input.files[0].size > +input.dataset.maxfilesize * 1024) {
error =
input.dataset.maxfilesizeMessage ? input.dataset.maxfilesizeMessage : 'File is too large';
input.classList.add('error');
}
Следующий оператор if также предназначен для ввода файла. Нам нужно проверить, является ли тип ввода файлом, имеет ли ввод атрибут data-allowed-types и не включает ли значение атрибута data-allowed-types значение типа ввода. Я разбиваю значение типа ввода, потому что по умолчанию это возвращает что-то вроде «image/png», а меня интересует только «png». Если все это верно, мы можем показать сообщение об ошибке.
// Check if input is file input and if it has data-allowed-types attribute and if file type is not equal to one of the values in data-allowed-type attribute
if(!error && input.type === 'file' && input.dataset.allowedTypes !== undefined && !input.dataset.allowedTypes.includes(input.files[0].type.split('/')[1])) {
error =
input.dataset.allowedTypesMessage ? input.dataset.allowedTypesMessage : 'Invalid file type';
input.classList.add('error');
}
Последнее, что нужно сделать, это проверить, установлена ли ошибка, если это не так, мы удаляем класс ошибки и скрываем сообщение об ошибке, в противном случае мы устанавливаем содержимое блока ошибок в сообщение об ошибке, а затем показываем ошибку. Нам не нужно добавлять здесь класс ошибок, потому что мы делаем это в других операторах is. И в конце функции validateInput мы возвращаем ошибку (нулевое значение или сообщение об ошибке), чтобы использовать это значение в функции submitForm.
И это все, что нам нужно сделать. Как видите, это на самом деле не сложно. И вы можете легко добавить новые валидаторы внутри (новые операторы if).