Попробую рассказать о процессе валидации описанной в теоретической части практикума. Для начала хочу напомнить как работает скрипты. Все что мы пишем вне функций выполняется один раз после загрузки скрипта (после загрузки страницы). Все что мы пишем в функциях выполняеся только когда они вызываются. Этот вызов может быть написанный нами, тогда она выполнится в момент вызова. А может быть назначена обработчиком события, тогда будет вызываться каждый раз когда возникнет событие. Хоть код и выполняется сверху вниз, принято описывать функции до их вызова. Поэтому если читать функции сверху вниз - можно запутаться. Читать скрит нужно с конца файла, где у нас должен быть расположен код который выполняется один раз при загрузке страницы. Там смотреть какие функции вызываются сразу, какие обработчики и по каким событиям подключаются.
Сигнализировать пользователю в реальном времени (а не в момент отправки данных формы) о том, что введены некорректные данные. Причем в контексте. Если ошибка в поле ввода, то описане ошибки должно выводится рядом с ним. В случае некоррекных данных в поле ввода нужно выделять поле с ошибкой (сделать цвет нижней границы красным) и под полем вывести диагностическое сообщение о том что нужно сделать пользователю, чтобы он указал корректные данные. В случае если хотя бы одно поле формы заполнено некорретно, то не должна быть доступна кнопка отправки данных формы.
Итак, что нам нужно чтобы произвести валидацию
- В разметке определить классы для оформления поля ввода с ошибкой, недоступной кнопки. Добавить текстовые элементы для вывода текста ошибки, определить класс для оформления и класс-модификатор для отображания. Для формы добавить атрибут novalidate, чтобы отключить показ всплывающих ошибок при отправке данных формы. Для полей ввода атрибутами определить какие введенные значения считать корректными.
- Подключить обработчики изменения полей ввода. Когда пользовать вводит в поле ввода текст или очищает его возникает событие input. По этому событию нам нужно
- 2.1. Проверить что в поле введены корректные данные
- 2.1.1. Если данные корректны:
- 2.1.1.1. убрать класс у поля ввода для выделения поля с ошибкой (красная граница),
- 2.1.1.2. найти связанный с ним текстовый элемент с ошибкой, очистить его текстовое содержимое и скрыть его (удалить класс модификатор показывающий элемент).
- 2.1.2. Если в поле ошибка, то наоборот
- 2.1.1.1. Добавить класс у поля ввода для выделения поля с ошибкой (красная граница),
- 2.1.1.2. найти связанный с ним текстовый элемент с ошибкой, установить его текстовое содержимое значением из объекта браузерной валидации и скрыть его (удалить класс модификатор показывающий элемент).
- 2.1.1. Если данные корректны:
- 2.2. Запустить функцию которая проверяет что все поля в форме корректно заполнены и в зависимости от результата удалит или добавит класс и атрибут отключения доступноси кнопки отправки данных
В комментариях обозначил последовательность выполнения скрипта. Пункты со звездочкой не будут выполнены сразу при загрузке страницы. А будут выполнены по возникновению соотвествующего события.
const showInputError = (formElement, inputElement, errorMessage) => {
const errorElement = formElement.querySelector(`.${inputElement.id}-error`); // *21.1.1 Находим элемент в группе полей с классом содержащим идентификатор поля и суфикс -error (связанный с полем ввода span)
inputElement.classList.add('form__input_type_error'); // *21.1.2 Для поля ввода с ошибкой добавляем класс form__input_type_error в котором мы определяем его оформление в случае ошибки (например цвет границы)
errorElement.textContent = errorMessage; // *21.1.3 Устанавливаем текстовое содержимое связанного спана текстом ошибки из параметра errorMessage
errorElement.classList.add('form__input-error_active'); // *21.1.4 Для связанного спана добавляем класс в стилях которого определена его видимость (без него он скрыт)
};
const hideInputError = (formElement, inputElement) => {
const errorElement = formElement.querySelector(`.${inputElement.id}-error`); // *21.2.1 Находим элемент в группе полей с классом содержащим идентификатор поля и суфикс -error (связанный с полем ввода span)
inputElement.classList.remove('form__input_type_error'); // *21.2.2 Для поля ввода с ошибкой УДАЛЯЕМ класс form__input_type_error в котором мы определяем его оформление в случае ошибки (например цвет границы). Без него оформление поле ввода будет как задано в классе элемента.
errorElement.classList.remove('form__input-error_active'); // *21.1.4 Для связанного спана Удаляем класс в стилях которого определена его видимость. Скрываем элемент.
errorElement.textContent = ''; // *21.1.4 Обнуляем текстовое содержимое спана. Ошибки нет.
};
const checkInputValidity = (formElement, inputElement) => {
if (!inputElement.validity.valid) { // *21 Анализируем прошло ли проверку валидации значение содержащееся в поле ввода inputElement
showInputError(formElement, inputElement, inputElement.validationMessage); // *21.1 Если НЕТ, то вызывается функция showInputError. В функцию передаем значение сообщения об ошибке сгенерированного браузером (свойство validationMessage поля ввода inputElement)
} else {
hideInputError(formElement, inputElement); // *21.2 Если ДА, то вызывается функция hideInputError
}
};
const hasInvalidInput = (inputList) => {
return inputList.some(input => !input.validity.valid) //14. Используем для перебора значений массива полей ввода функцию some(). Для каждого поля из массива проверяем корректно ли его содержимое. Если хотя бы одно из полей неккоретно, то some вернет Истину. Иначе Ложь. Это же значение мы вернем из функции.
};
const toggleButtonState = (inputList, buttonElement) => {
if (hasInvalidInput(inputList)) { // 13. Вызываем функцию hasInvalidInput с передачей в нее массива полей ввода группы полей. Анализируем возвращенное функцией значение
buttonElement.classList.add('button_inactive'); // 15.1 Если функция hasInvalidInput вернула Истина, то есть есть некорректные поля, то добавлять класс который оформляет кнопку в состояние неактивной. Примечение: В проектной работе дополнительно нужно добавить атрибут disabled
} else {
buttonElement.classList.remove('button_inactive'); // 15.2 Если функция hasInvalidInput вернула Ложь, то есть все поля заполнены корректно, то удалить класс который оформляет кнопку в состояние неактивной. Примечение: В проектной работе дополнительно нужно удалить атрибут disabled
}
};
const setEventListeners = (formElement) => {
const inputList = Array.from(formElement.querySelectorAll('.form__input')); // 9. Получаем коллекцию всех элементов группы полей с классом form__input. Преобразуем его в массив(иначе нам будет недоступен метод some())
const buttonElement = formElement.querySelector('.form__submit'); // 10. Находим кнопку в группе полей с классом form__submit.
toggleButtonState(inputList, buttonElement); // 11. Вызываем функцию toggleButtonState с передачей в нее массива найденных полей ввода и кнопки. Нужно для установки недоступного состояния кнопки при загрузке страницы.
inputList.forEach((inputElement) => { // 16. Обходим массив найденных полей ввода. Для каждого поля ввода (inputElement) выполняем код в фигурных скобках
inputElement.addEventListener('input', function () { // 17. Подключаем обработчик события input для поля ввода.
checkInputValidity(formElement, inputElement); // *20. Тут начинаю новую нумерацию, так как код будет выполнен только когда возникнет событие input. Вызывается функция checkInputValidity с передачей в нее группы полей и поля ввода на котором возникло событие input.
toggleButtonState(inputList, buttonElement); // *21. Вызываем функцию toggleButtonState. Будут выполненны пп 13-15
});
});
};
const enableValidation = () => {
const formList = Array.from(document.querySelectorAll('.form')); // 2. Получаем коллекцию всех элементов документа с классом form. Преобразуем его в массив
formList.forEach((formElement) => { // 3. Перебираем все найденные формы. Для каждой формы (formElement) выполняем блок кода в фигурных скобках.
formElement.addEventListener('submit', function (evt) { // 4. Для каждой формы (formElement) подключаем обработчик отправки данных формы. Примечание: В проектной работе у нас уже есть эти обработчики поэтому второй раз подключать не нужно.
evt.preventDefault(); // *5. При отправке данных формы отключить стандартные действия браузера.
});
const fieldsetList = Array.from(formElement.querySelectorAll('.form__set')); //6. Находим коллекцию всех элементов документа с классом form__set. Преобразуем его в массив. В проектной работе этого не нужно. Можем обходить массив найденных форм.
fieldsetList.forEach((fieldSet) => { //7. Обходим массив найденных групп полей и для каждого
setEventListeners(fieldSet); //8. Вызываем функцию setEventListeners передав в нее текущую группу полей.
});
});
};
enableValidation(); // 1. Это единственная строчка кода которая лежит вне функции. При загрузке страницы произойдет вызов функции enableValidation()
При решении проектной работы обратите внимание, что пример в тренажере приведен со статичной страницей. Форма с полями ввода и кнопкой всегда отображена на странице. Поэтому в ней допустимо управлять состоянием кнопки при загрузке страницы. В нашем случае у нас формы в всплывающих окошках. Поэтому управлять видимостью нужно если решать в лоб то при открытии соотвествующего модального окна. Также интересное решение придумала Валентина Карпова: обновлять состяние валидации по событию reset формы. Также в задании указано, что нам нужно сделать механихм более универсальным. Всю специфику нашего документа, такие как селекторы и имена классов необзоидмо вынести в объект с "настройками" и передать его в функцию enableValidation()