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

document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById("form");
form.addEventListener('submit', formSend);

    async function formSend(e) {
        e.preventDefault();

        let error = formValidate(form);

        let formData = new FormData(form);

        if (error === 0 ) {
            form.classList.add('_sending');
            let response = await fetch ('sendmail.php', {
                method: 'POST', 
                body: formData
            })
            if (response.ok) {
                let result = await response.json();
                alert(result.message);
                form.reset ();
            } else {
                alert ('Кажется что-то пошло не так')
            }
        } else {
          alert ('Если хочешь отправить нам сообщение, то заполни все поля, пожалуйста)');
        }
    }

    function formValidate(form) {
        let error = 0;
        let formReq = document.querySelectorAll('._req')

        for (let index = 0; index < formReq.length; index++){
            const input = formReq[index];
            formRemoveError(input);

            if(input.classList.contains('_email')){
                if(emailTest(input)){
                    formAddError(input);
                    error++;
                }
            } else {
                if (input.value === ""){
                    formAddError(input);
                    error++;
                }
            }
        }
        return error;
    }
    function formAddError(input) {
        input.parentElement.classList.add('_error');
        input.classList.add('_error')
    }
    function formRemoveError(input) {
        input.parentElement.classList.remove('_error');
        input.classList.remove('_error');
    }
    function emailTest(input) {
        return !/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,8})+$/.test(input.value);
    }

})
<HTML>
  <BODY>
    <form id="form" action="#" class="form_body">
      <div class="form_item">
          <label class="form_label _req" for="formName">Твоё имя</label>
          <input type="text" name="name" id="formName" class="form_input">
      </div>
      <div class="form_item">
          <label class="form_label _email _req" for="formEmail">Твоя почта</label>
          <input type="text" name="email" id="formEmail" class="form_input">
      </div>
      <div class="form_item">
          <label class="form_message _req" for="formEmail">Твоё сообщение</label>
          <textarea name="message" id="formMessage" class="form_label"></textarea>
      </div>
      <button type="submit" class="form_button">Отправить</button>
    </form>
  </BODY>
</HTML>


Ответы (1 шт):

Автор решения: DiMithras

Здравствуй, Настя.

Не наговаривай на форму, форма всё видит ?
Давай разберёмся в проблеме с твоим кодом. Рассмотрим что выдаст такой вот :

const label = document.getElementById("form");
let formReq = document.querySelectorAll('._req')

for (let index = 0; index < formReq.length; index++){
  const label = formReq[index];
  console.log(label)
}
Вывод:
<label class="form_label _req" for="formName"></label>
<label class="form_label _email _req" for="formEmail"></label>
<label class="form_message _req" for="formEmail"></label>

Я намеренно переименовал переменную input из твоего кода в label, т.к. в переменную записывается именно этот тег.

<label> не содержит пользовательского ввода сам по себе, данный тег используется для установки связи между меткой и элементом формы (<input>, <select>, <textarea>).
Подробнее можно почитать тут. Или ещё чуть подробнее на английском про создание связи тут.

У тебя связь выстроена почти правильно, у каждой <label> есть атрибут for который ссылается на ввод. Обрати внимание на <textarea> c id formMessage, там связь неправильная, <label> ссылается на formEmail.

Поправим эту ошибку в и запустим несколько видоизменённый код:

const form = document.getElementById("form");
let formReq = document.querySelectorAll('._req')

for (let index = 0; index < formReq.length; index++){
  const label = formReq[index];
  const input = document.getElementById(label.htmlFor);
  console.log(input);
}
Вывод:
<input type=​"text" name=​"name" id=​"formName" class=​"form_input">​
<input type=​"text" name=​"email" id=​"formEmail" class=​"form_input">​
<textarea name=​"message" id=​"formMessage" class=​"form_label">​</textarea>​

Вот и чудесно, теперь обращаемся к <input>, с которого как раз и будем брать значения.

По итогу, что необходимо сделать, чтобы было чудесно:

  1. Поправить атрибут for в 3-ем <label> и вписать туда formMessage.
  2. Объявить в цикле переменную label и переписать присвоение переменной input.
  3. В if(input.classList.contains('_email')){ заменить переменную с input на label.

Альтернатива

В качестве альтернативы, можешь переместить классы _req и _email с <label> на <input> и <textarea> соответственно, тогда в коде ничего менять не надо будет.
К тому же ранее предложенное решение будет работать только с правильно прописанными атрибутами for, которые у тебя прописаны неправильно. Да и <label> ты нигде не используешь, поэтому есть смысл сразу напрямую обращаться к вводу.

<form id="form" action="#" class="form_body">
  <div class="form_item">
    <label class="form_label">
      Твоё имя
      <input type="text" name="name" class="form_input _req">
    </label>
  </div>
  <div class="form_item">
    <label class="form_label">
      Твоя почта
      <input type="text" name="email" class="form_input _email _req">
    </label>
  </div>
  <div class="form_item">
    <label class="form_message">
      Твоё сообщение
      <textarea name="message" class="form_label _req"></textarea>
    </label>
  </div>
  <button type="submit" class="form_button">Отправить</button>
</form>

Здесь я атрибуты id и for убрал, т.к. использую альтернативную связку, подробнее в первой ссылке:

Существует два способа связывания объекта и метки. Первый заключается в использовании идентификатора id внутри элемента формы и указании его имени в качестве атрибута for тега <label>. При втором способе элемент формы помещается внутрь контейнера <label>.

Если атрибут id где-то ещё используется, то можно его оставить. После переноса классов можно не менять.
Но поменять его всё равно следует, т.к. в текущем состоянии при неправильном вводе почты сообщение будет как при пустом вводе, что совсем не user-friendly. Но это уже выходит за рамки заданного вопроса.

→ Ссылка