#8 Валидация формы в Javascript

poster
В этом уроке мы с вами напишем валидацию формы на javascript. То есть мы будем проверять данные и условия во всех полях, и если в них есть ошибки, то выводить их на экран.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

Мы с вами изучили базовые методы для работы с DOM. в заключение хотелось бы на практике применить то, чему мы научились.

Поэтому в этом уроке мы с вами напишем валидацию формы на javascript. То есть мы будем проверять данные и условия во всех полях, и если в них есть ошибки, то выводить их на экран.

Давайте добавим нашу форму. Главное, что нам нужно сделать, это добавить классы на все елементы формы, с которыми нам прийдется взаимодействовать.

<form class='formWithValidation'>
  <div>
    <div>
      <label>From:</label>
    </div>
    <div>
      <input type='text' class='from' />
    </div>
  </div>
  <div>
    <div>
      <label>Password:</label>
    </div>
    <div>
      <input type='password' class='password' />
    </div>
  </div>
  <div>
    <div>
      <label>Password confirmation:</label>
    </div>
    <div>
      <input type='password' class='passwordConfirmation' />
    </div>
  </div>
  <div>
    <div>
      <label>Where</label>
    </div>
    <div>
      <select class='where'>
        <option></option>
        <option value='developers'>Developers</option>
        <option value='managers'>Managers</option>
        <option value='devops'>DevOps</option>
      </select>
    </div>
  </div>
  <div>
    <div>
      <label>Message:</label>
    </div>
    <div>
      <textarea class='message'></textarea>
    </div>
  </div>
  <input type='submit' class='validateBtn' value='Validate'/>
</form>

Теперь давайте писать валидацию формы. Сначала мы бы хотели найти все элементы, с которыми мы будем работать, но мы хотим их не просто искать в DOMе, а только внутри класса formWithValidation, так как это обезопашивает нас от того, что такие классы будут использоваться где-то еще.

Давайте найдем с вами кнопку Validate

var validateBtn = document.querySelector('.formWithValidation .validateBtn')

Как вы видите, каждый раз, когда мы захотим находить елемент внутри formWithValidation, нам прийдется указывать его в querySelector. Старайтесь всегда избегать лишних и ненужных повторений кода.

В данном случае мы можем вынести поиск formWithValidation отдельно и все остальные елементы искать относительно него

var form = document.querySelector('.formWithValidation')
var validateBtn = form.querySelector('.validateBtn')

Так намного читабельнее. Теперь давайте начнем писать код, а остальные елементы будем добавлять по мере надобности.

Сейчас нам нужно повесить евент submit на нашу форму. Тогда при нажатии enter на любом поле либо на клик Validate, форма отправится. Мы с вами это уже делали

form.addEventListener('submit', function () {
  console.log('clicked on validate')
})

Если мы посмотрим в браузер, то происходит следующее. Мы видим наш console.log, но только на доли секунды. Это происходит потому, что html по умолчанию отправляет форму и перезагружает при этом страницу.

Нам в javascript, это совсем не нужно. Для этого существует метод preventDefault. То есть он запрещает поведение по умолчанию. В функции, которая срабатывает у нас при submit, первым параметром нам приходит event. На нем то нам и нужно вызвать eventPreventDefault.

form.addEventListener('submit', function (event) {
  event.preventDefault()
  console.log('clicked on validate')
})

Если мы посмотрим сейчас, то у нас срабатывает submit и выводится console.log.

Теперь для начала давайте прочитаем значения всех полей при валидации формы.

Начнем в from

var form = document.querySelector('.formWithValidation')
var validateBtn = form.querySelector('.validateBtn')
var from = form.querySelector('.from')

form.addEventListener('submit', function (event) {
  event.preventDefault()
  console.log('clicked on validate')
  console.log('from: ', from.value)
})

Если мы посмотрим в браузер, у нас вывелось текущее значение поля from. То же самое сделаем с всеми полями.

var form = document.querySelector('.formWithValidation')
var validateBtn = form.querySelector('.validateBtn')
var from = form.querySelector('.from')
var password = form.querySelector('.password')
var passwordConfirmation = form.querySelector('.passwordConfirmation')
var passwordConfirmation = form.querySelector('.passwordConfirmation')
var where = form.querySelector('.where')
var message = form.querySelector('.message')

form.addEventListener('submit', function (event) {
  event.preventDefault()
  console.log('clicked on validate')
  console.log('from: ', from.value)
  console.log('password: ', password.value)
  console.log('passwordConfirmation: ', passwordConfirmation.value)
  console.log('where: ', where.value)
  console.log('message: ', message.value)
})

Теперь мы хотим проверить, что все поля у нас заполнены. Мы бы могли написать кучу if условий в стиле

if (!from.value) {
  // show from error
}
if (!password.value) {
  // show passoword error
}

Но это огромное количество кода, которое мы можем упростить. Мы можем пройтить по всем елементам, которые у нас есть и проверить пустой или нет. Для того, чтобы это сделать давайте добавим на каждый елемент формы одинаковый класс. например field.

Например

<input type='text' class='from field' />

Теперь мы можем найти все елементы сразу и пройти по ним циклом, чтобы проверить заполнено ли поле.

var fields = form.querySelectorAll('.field')

form.addEventListener('submit', function (event) {
  event.preventDefault()

  for (var i = 0; i < fields.length; i++) {
    if (!fields[i].value) {
      console.log('field is blank', fields[i])
    }
  }
})

Если мы посмотрим в браузер, то нам в консоль вывелись ошибки. И теперь хотелось бы вывести эти ошибки на форму. Мы можем сгенерировать новый елемент и добавим к каждому полю, которое не заполнено.

Давайте создадим новые елемент. Добавим еще красный цвет и текст Cannot be blank. Теперь, чтобы вставить его перед нашими полями используем insertBefore. А так как нам нужно указать парента, то используем .parentElement.

for (var i = 0; i < fields.length; i++) {
  if (!fields[i].value) {
    console.log('field is blank', fields[i])
    var error = document.createElement('div')
    error.className='error'
    error.style.color = 'red'
    error.innerHTML = 'Cannot be blank'
    form[i].parentElement.insertBefore(error, fields[i])
  }
}

Если мы посмотрим в браузер, то у нас вывелась валидация всех полей.

Но если мы нажмем еще раз validate, то все наши сообщения сдублируются. Самый простой способ этого избежать, это удалять все ошибки с страницы при валидации.

form.addEventListener('submit', function (event) {
  event.preventDefault()

  var errors = form.querySelectorAll('.error')

  for (var i = 0; i < errors.length; i++) {
    errors[i].remove()
  }

  for (var i = 0; i < fields.length; i++) {
    if (!fields[i].value) {
      console.log('field is blank', fields[i])
      var error = document.createElement('div')
      error.className = 'error'
      error.style.color = 'red'
      error.innerHTML = 'Cannot be blank'
      form[i].parentElement.insertBefore(error, fields[i])
    }
  }
})

Если мы посмотрим в браузер, то ошибки перестали дублироваться.

Теперь давайте добавим проверку на ошибку, когда у нас не совпадают пароли. Просто напишем условие, что пароли не совпадают, а внутри создадим новый error и добавим перед паролем.

form.addEventListener('submit', function (event) {
  event.preventDefault()

  var errors = form.querySelectorAll('.error')

  for (var i = 0; i < errors.length; i++) {
    errors[i].remove()
  }

  for (var i = 0; i < fields.length; i++) {
    if (!fields[i].value) {
      console.log('field is blank', fields[i])
      var error = document.createElement('div')
      error.className = 'error'
      error.style.color = 'red'
      error.innerHTML = 'Cannot be blank'
      form[i].parentElement.insertBefore(error, fields[i])
    }
  }

  if (password.value !== passwordConfirmation.value) {
    console.log('not equals')
    var error = document.createElement('div')
    error.className = 'error'
    error.style.color = 'red'
    error.innerHTML = 'Passwords doesnt match'
    password.parentElement.insertBefore(error, password)
  }
})

Если мы посмотрим в браузер, то когда пароли разные, у нас выводится ошибка.

Теперь хотелось бы сделать этот код понятнее, так как у сейчас его сложно читать и поддерживать. Давайте для начала создадим функцию, которая будет принимать на вход строку и возвращать DOM елемент.

var generateError = function (text) {
  var error = document.createElement('div')
  error.className = 'error'
  error.style.color = 'red'
  error.innerHTML = text
  return error
}

И теперь мы можем убрать повторяющийся код

form.addEventListener('submit', function (event) {
  event.preventDefault()

  var errors = form.querySelectorAll('.error')

  for (var i = 0; i < errors.length; i++) {
    errors[i].remove()
  }

  for (var i = 0; i < fields.length; i++) {
    if (!fields[i].value) {
      console.log('field is blank', fields[i])
      var error = generateError('Cant be blank')
      form[i].parentElement.insertBefore(error, fields[i])
    }
  }

  if (password.value !== passwordConfirmation.value) {
    console.log('not equals')
    var error = generateError('Password doesnt match')
    password.parentElement.insertBefore(error, password)
  }
})

Теперь давайте вынесем в отдельную функцию очистку ошибок.

var removeValidation = function () {
  var errors = form.querySelectorAll('.error')

  for (var i = 0; i < errors.length; i++) {
    errors[i].remove()
  }
}

И вызовем ее

form.addEventListener('submit', function (event) {
  event.preventDefault()

  removeValidation()

  for (var i = 0; i < fields.length; i++) {
    if (!fields[i].value) {
      console.log('field is blank', fields[i])
      var error = generateError('Cant be blank')
      form[i].parentElement.insertBefore(error, fields[i])
    }
  }

  if (password.value !== passwordConfirmation.value) {
    console.log('not equals')
    var error = generateError('Password doesnt match')
    password.parentElement.insertBefore(error, password)
  }
})

И вынесем проверку полей на пустоту

var checkFieldsPresence = function () {
  for (var i = 0; i < fields.length; i++) {
    if (!fields[i].value) {
      console.log('field is blank', fields[i])
      var error = generateError('Cant be blank')
      form[i].parentElement.insertBefore(error, fields[i])
    }
  }
}

И вызовем ее

form.addEventListener('submit', function (event) {
  event.preventDefault()

  removeValidation()

  checkFieldsPresence()

  if (password.value !== passwordConfirmation.value) {
    console.log('not equals')
    var error = generateError('Password doesnt match')
    password.parentElement.insertBefore(error, password)
  }
})

И вынесем валидацию пароля

var checkPasswordMatch = function () {
  if (password.value !== passwordConfirmation.value) {
    console.log('not equals')
    var error = generateError('Password doesnt match')
    console.log(error)
    password.parentElement.insertBefore(error, password)
  }
}

Вот теперь наш код намного проще читать

form.addEventListener('submit', function (event) {
  event.preventDefault()

  removeValidation()

  checkFieldsPresence()

  checkPasswordMatch()
})

Итак в этом уроке мы с вами научились валидировать формы на javascript.

Если у вас что-то не получается, либо возникли вопросы и комментарии, пишите их прямо под этим видео и я обязательно на них отвечу.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
Никита Медведев
2 лет назад
Добрый вечер, подскажите как добавить условия для проверки пароля на кол-во символов, обязательно заглавную букву и без пробелов, так же установить для имени
Orken Toibazarov
3 лет назад
Здавствуйте! а в какой последовательности смотреть Серии ? например после этой серий
monsterlessons
3 лет назад
Добрый день. В той последовательности, в которой они идут в серии. https://monsterlessons.com/project/series/rabota-s-dom-derevom-v-javascript
NATALYA MYUSTER
5 лет назад
А как поставить все лейблы слева?
NATALYA MYUSTER
5 лет назад
Spasibo bolshoe !!! Ochen interesno i vse ochen ponyatno !
stayacid
5 лет назад
Отличный урок, очень доступно и подробно написано, буду дальше учиться по вашим статьям и видео
jexon
5 лет назад
скопировал ваш код (js html )все подключил постоянно ругается . на Uncaught TypeError: Cannot read property 'querySelector' of null at login.js:2 вторая строка кода . я только вникаю в тему так что не ругайте сильно)
jexon
5 лет назад
все нашел косяк тупа подключил не в конец html.
monsterlessons
5 лет назад
Отлично, что сами разобрались. Ну да, дом элементов еще не было, когда отработал javascript. P.S. вы правы, validateBtn нам не нужна
jexon
5 лет назад
так зачем нам эта переменная : validateBtn если мы ее не используем
Gleb
6 лет назад
Урок супер! Но у меня появился вопрос. Как мне написать функцию,которая после того как все данные заполнены корректно, очищает форму и вызывает модальное окно? Мне не нужна отправка на сервер. Просто очистка и вызов модалки. Спасибо!
monsterlessons
6 лет назад
Вы имеете ввиду кастомное модальное окно? У вас должна быть какая то доп функция которая открывает модальное окно ну и зависит от того используете ли вы библиотеку для модалок или нет. Внутри функции сабмит 1. делаем eventPreventDefault(), чтобы форма не отправлялась 2. валидируем поля 3. если все валидно, то чистим поля 4. открываем модалку. form.addEventListener('submit', function (event) { event.preventDefault() removeValidation() checkFieldsPresence() checkPasswordMatch() removeValidation() openModal() })
Gleb
6 лет назад
По поводу функции для окна понял. А по очистке нет. Как проверить что все поля валидны?
monsterlessons
6 лет назад
Можно сделать разными вариантами с разной сложностью. Самый просто из каждой функции на проверку валидации возвращать true/false и тогда var allPresent = checkFieldsPresence() var passwordMatch = checkPasswordMatch() и потом if (allPresent && passwordMatch) {openModal()} Более продвинутый вариант, это класс валидатор, у которого будет метод isValid и методы checkPasswordMatch, checkFieldsPresence и который будет хранить состояние валидности формы. Это позволит разделить проверку isValid и подсветку полей.
Gleb
6 лет назад
Все сделал как у вас , к сожалению что то идет не так. При использование вашего кода в функцию сабмит происходит это: я сделал else для того что бы вывести сообщение в консоль что форма не валидна. В начале когда форма не заполнена консоль вывод 'не валидна' , после ее заполнения надпись не меняется и по прежнему выводится в консоле что 'не валидна'. form.addEventListener('submit', function (event) { event.preventDefault() removeValidation() var allPresent = checkFieldsPresence() var passwordMatch = checkPasswordMatch() if (allPresent && passwordMatch) { console.log('Valid'); } else { console.log('Invalid'); } })
Gleb
6 лет назад
Посмотрите пожалуйста.
monsterlessons
6 лет назад
Так сложно сказать. Подебажте. Выведите allPresent и passwordMatch. Посмотрите что не так.
psevdobrat
6 лет назад
Дякую за урок! Навіщо перевіряти поле password і в ньому виводити помилку невідповідності паролів, якщо правильніше це робити для поля passwordConfirmation
monsterlessons
6 лет назад
На здоровье. Делайте, как на ваш взгляд юзеру удобнее. Это просто пример.
YanaVG
6 лет назад
Дякую за урок! Все дуже зрозуміло пояснено!
monsterlessons
6 лет назад
На здоровье)
galya
6 лет назад
Привет! Спасибо за урок. А подскажите, есть у вас пример как добавить в этот код чтобы бордер у инпутов становился красным если поле незаполнено? спасибо!
monsterlessons
6 лет назад
На здоровье. Вы можете после insertBefore делать fields[i].style.border = '1px solid red'. И, также, очищать все бордеры у инпутов в removeValidation
galya
6 лет назад
спасибо большое! ) я пока еще совсем зеленый junior и информация очень полезная.
Chapaev611
6 лет назад
Здравствуйте! Спасибо за урок! Скажите, а как после того когда форма заполнена отправлять запрос !? Т.е. у нас форма заполнена, но отправки данных нет, т.к. мы написали event.preventDefault() !?
monsterlessons
6 лет назад
Добрый день. На здоровье) Обычно запросы отправляют с помощью XmlHttpRequest, так как это позволяет легко конролировать запросы на клиенте. То есть, в месте, где валидация успешно прошла, мы вызываем обращение к серверу. Вот урок о XmlHttpRequest: https://monsterlessons.com/project/lessons/poluchaem-dannye-v-javascript-s-pomoshyu-xmlhttprequest Либо с использованием jQuery: https://monsterlessons.com/project/lessons/poluchenie-dannyh-ot-servera-v-jquery
Chapaev611
6 лет назад
Спасибо большое за ответ!Я просмотрел ссылки, которые вы дали, но все равно не понимаю как отправить форму на сервер, если мы ничего не возвращаем !? Мы проверяем форму только на заполнение каких-либо данных, но не возвращаем результаты. form.addEventListener('submit', function(event) { event.preventDefault(); removeValidation(); checkFieldsPresence(); checkPasswordMatch(); }); P.S. Как в данном примере вызвать обращение !?
monsterlessons
6 лет назад
Я напишу на jQuery, чтобы не писать так много кода, как с XmlHttpRequest: form.addEventListener('submit', function(event) { // тут все проверки на валидацию if (successValidation) { $.ajax({ method: 'POST', url: "http://someapi.com", data: { where: where.value } }) } }
Chapaev611
6 лет назад
Спасибо Вам большое! Вот теперь понятно как можно было сделать!
Kogoruhn
7 лет назад
Здравствуйте! Прокомментируйте, пожалуйста, и мои вопросы тоже (под этим видео)
Геннадий Горбулин
7 лет назад
Спасибо за урок. Относительно применения для реальных проектов. Это правда, что innerHTML считается небезопаcной, т.к. существует возможность вставки html кода вместе со скриптом? Стоит ли использовать textContent или слухи об опасностях innerHTML слишком преувеличены? Также есть вопрос по циклам. Цикл while при измерениях дает наивысшую скорость, forEach легче читать - какой критерий предпочтительнее в проектах?
monsterlessons
7 лет назад
innerHTML достаточно безопасна, если экранировать содержимое. Всегда лучше выбирать читабельность, а не скорость. Не нужно заниматься оптимизацией, пока ничего не тормозит.
Дмитрий Мищенко
7 лет назад
А когда уже будет интернет магазин react + redux?
monsterlessons
7 лет назад
Скоро. Надеюсь к концу мая или начала июня уже выложить.
Дмитрий Мищенко
7 лет назад
А еще подскажите пожалуйста, чтобы найти работу на реакте обязательно знать node.js ( и если да то на каком уровне)? Я сейчас работаю верстальщиком и хочу выучить реакт и по нему работать...
monsterlessons
7 лет назад
Не обязательно, обычно зависит от компании. Ну хотя бы на уровне как установить пакеты знать нужно.
Kogoruhn
7 лет назад
Спасибо за отличный урок! Для меня, как начинающего, просто сокровище) Остались вопросы: Почему мы тут для generateError, removeValidation, checkFieldsPresence и checkPasswordMatch используем function expression а не declaration? Второй вариант вызовет меньше ошибок же. И еще: В самом низу у нас получилась короткая запись с addEventListener и вызовом функций. А само их описание осталось выше по коду или вы каждую функцию в отдельный js выносите?
monsterlessons
7 лет назад
Спасибо. Извините, не увидел раньше ваш комментарий. 1. В принципе нет большой разницы использовать ли function expression либо declaration. Главное, чтобы вы использовали что-то одно и понимали разницу между ними. В данном случае оба варианта не вызовут ошибок. 2. Главное тут здравый смысл. Я вижу смысл бить на отдельные файлы, когда он разрастается. Хотя бы 50+ строк.
Kogoruhn
7 лет назад
Понял, спасибо!)