表单验证:验证字段后无法启用提交按钮

问题描述 投票:0回答:1

我正在为朋友(我根本不是程序员)制作一个表单(html、css、javascript 和 jquery,用于验证邮政编码),我认为我的所有验证都是正确的。

事实是,表单已禁用提交按钮,如果所有验证都通过,则应启用该按钮,但这种情况不会发生。

控制台中没有任何内容,不知道我错过了什么。

这是代码:

// DISABLE THE SUBMIT
const submitButton = document.getElementById('submitId');
submitButton.disabled = true;

// NOME
function validateName() {
  const nameInput = document.getElementById('nomeId');
  const nameError = document.getElementById('nomeError');

  nameInput.addEventListener('input', () => {
    const nome = nameInput.value.trim();
    const nameParts = nome.split(' ');

    if (nome.lenght === 0) {
      nameError.textContent = "";
    } else if (nameParts.length < 2 || /\d/.test(nome)) {
      nameError.textContent = "✖";
      return false;
    } else {
      nameError.textContent = "✓";
      return true;
    }
  });
}
validateName();

// CPF
function validateCPF() {
  const cpfInput = document.getElementById('cpfId');
  const cpfError = document.getElementById('cpfError');
  const cpfRegex = /^\d{11}$/;

  cpfInput.addEventListener('input', () => {
    const cpf = cpfInput.value.trim();

    if (cpf.length === 0) {
      cpfError.textContent = "";
    } else if (!cpfRegex.test(cpf)) {
      cpfError.textContent = "✖";
      return false;
    }

    // Calcula o primeiro dígito verificador
    let soma = 0;
    for (let i = 1; i <= 9; i++) {
      soma += parseInt(cpf.substring(i - 1, i)) * (11 - i);
    }
    let resto = (soma * 10) % 11;
    if (resto === 10 || resto === 11) {
      resto = 0;
    }
    if (resto !== parseInt(cpf.substring(9, 10))) {
      cpfError.textContent = "✖";
      return false;
    }

    // Calcula o segundo dígito verificador
    soma = 0;
    for (let i = 1; i <= 10; i++) {
      soma += parseInt(cpf.substring(i - 1, i)) * (12 - i);
    }
    resto = (soma * 10) % 11;
    if (resto === 10 || resto === 11) {
      resto = 0;
    }
    if (resto !== parseInt(cpf.substring(10, 11))) {
      cpfError.textContent = "✖";
      return false;
    } else {
      cpfError.textContent = "✓";
      return true;
    }
  });
}
validateCPF();

// TELEFONE
function validatePhone() {
  const phoneInput = document.getElementById('telefoneId');
  const phoneError = document.getElementById('telefoneError');
  const phoneRegex = /^(1[1-9]|2[12478]|3[1-578]|4[1-9]|5[13-5]|6[1-9]|7[13-579]|8[1-9]|9[1-9])9\d{8}$/;

  phoneInput.addEventListener('input', () => {
    const phone = phoneInput.value.trim();

    if (phone.length === 0) {
      phoneError.textContent = ""; // Clear error message
    } else if (!phoneRegex.test(phone)) {
      phoneError.textContent = "✖";
      return false;
    } else {
      phoneError.textContent = "✓";
      return true;
    }
  });
}
validatePhone();

// CEP
function validateCEP() {
  const cepInput = document.getElementById('cepId');
  const cepError = document.getElementById('cepErro');

  const debounce = (func, delay) => {
    let timeoutId;
    return (...args) => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => func.apply(this, args), delay);
    };
  };

  const consultarCEP = debounce(() => {
    const cep = cepInput.value.trim(); // Corrected access
    if (cep.length === 8) {
      $.ajax({
        url: `https://viacep.com.br/ws/${cep}/json/`,
        dataType: 'json',
        success: function(resposta) {
          if (!resposta.erro) {
            cepError.textContent = "✓";
            return true;
          } else {
            cepError.textContent = "✖";
            return false;
          }
        },
        error: function() {
          cepError.textContent = "✖";
          return false;
        }
      });
    } else if (cep.length > 0) {
      cepError.textContent = "✖";
      return false;
    } else {
      cepError.textContent = "✓";
      return true;
    }
  }, 500); // Wait 500ms after last requisition
  cepInput.addEventListener('input', consultarCEP);
}
validateCEP();


function validateForm() {
  const nameValid = validateName1();
  const cpfValid = validateCPF();
  const phoneValid = validatePhone();
  const cepValid = validateCEP();

  submitButton.disabled = !(nameValid && cpfValid && phoneValid && cepValid);
}

window.addEventListener("load", function() {
  const form = document.getElementById('formId');

  form.addEventListener("submit", function(e) {
    e.preventDefault();
    validateForm();
    if (submitButton.disabled === false) { // Check if validation passed
      const data = new FormData(form);
      const action = e.target.action;
      fetch(action, {
          method: 'POST',
          body: data,
        })
        .then(() => {
          document.getElementById("submitId").value = "enviando";
          window.location.replace("redirect.html");
        });
    }
  });
});
body {
  font-size: 16px;
}

form {
  display: table;
  min-width: 400px;
  margin: 100px auto 0 auto;
  /*        background: red;*/
}

input {
  display: table;
  min-width: 400px;
  height: 25px;
}

label {
  display: inline-block;
  height: 25px;
  background: orange;
}

span {}

input[type=submit] {
  /*        display: none;*/
}
<form id="formId" method="post" action="https://script.google.com/macros/s/AKfycbyowTbATrI3EAYywexpEYl0wjcPlD5p4vPnPV1ViQ6bPMAO56jj8YqEWOyAP4jiQhh6Qw/exec">
  <input name="nome" id="nomeId" type="text">
  <label id="nomeLabel" for="nomeId">nome</label> <b id="nomeError"></b>
  <br><br>
  <input name="cpf" id="cpfId" type="text" inputmode="tel" maxlength="11">
  <label id="tcpfLabel" for="telefoneId">cpf <span id="cpfError"></span></label>
  <br><br>
  <input name="telefone" id="telefoneId" type="text" inputmode="tel" maxlength="11">
  <label id="telefoneLabel" for="telefoneId">telefone</label>
  <span id="telefoneError"></span>
  <br><br>
  <input name="cep" id="cepId" type="text" inputmode="tel" maxlength="8">
  <label id="cepLabel" for="cepId">cep</label>
  <span id="cepErro"></span>

  <input name="pre" id="pre" type="hidden" value="pre">

  <input id="submitId" type="submit" value="enviar">
</form>

这是验证其他功能的功能:

function validateForm() {
    const nameValid = validateName1();
    const cpfValid = validateCPF();
    const phoneValid = validatePhone();
    const cepValid = validateCEP();

    submitButton.disabled = !(nameValid && cpfValid && phoneValid && cepValid);
}

如果有人能给我一些启发,我会非常高兴和高兴。

提前谢谢您。

javascript forms validation
1个回答
0
投票

下表列出了以下示例中使用的内容:

JavaScript 类型 目的
.forms
HTML集合 轻松访问
<form>
.setCustomValidity()
方法 自定义错误消息。
.validationMessage
财产 属性版
.setCustomValidity()
活动委托 范式 管理多个
<input>
的事件处理。
"input"
活动 立即验证
<input>
数据。
.addEventListener()
方法 注册一个元素来侦听事件并在触发该事件时调用函数(事件处理程序)。
.target
财产 确定用户正在与哪个
<input>
进行交互。
.matches()
方法 通过将元素与给定的 CSS 选择器匹配来识别元素。
RegExp()
构造函数 根据
<input>
[pattern]
属性创建正则表达式。
.test()
方法 比较
<input>
.value
与它的
.pattern
值。
.classList
财产 切换
<input>
.valid
.invalid
类别。
:invalid
伪类没那么有用,因为
:valid
:empty
涵盖了太多条件。
CSS 类型 目的
+
后续兄弟组合器 参见
::before
::before
伪元素 对于 ✓ 和 ❌ 图标
HTML 类型 目的
pattern
属性 该值是一个正则表达式,充当验证的约束
<input>'s 
.value
.| |  |[
必填
][15]|Attribute|When 
true
it is a constraint to validate the
s validity when the 
“提交”
 event is triggered.| |  |[
最大长度
][16]|Attribute|A cap on the number of characters a 
  can accept.| |Ⅱ |[
行动
][17]|Attribute|The 
will submit it's data to a live test server. See
[目标]
| |  |[
目标
][18]|Attribute|If assigned to a 
, the value is where the server will send it's response when the POST is successful (200). The value in this particular 
is the
[名称]
of an
located under the
. The server's response will appear in the 
 as a JSON.| |Ⅲ |[ 
][19]|Element|1. It's a [form control][10]. &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; 2. It has a 
.值
property and it can hold content (e.g. text, elements, &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;etc). &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;3. It can display other form controls' calculations via the
[用于]
 &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8195;attribute.| |  |[
][20]|Element|1. A 
without a
[类型]
 attribute or with a &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8195; &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8198;&#8198;&#8198;&#8198;&#8198; &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;
[类型=“提交”]
attribute/value whilst inside a
will &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;trigger a
“提交”
event for said
 whenever it is &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;
“点击”
ed. &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8198;&#8198;&#8198;&#8198;&#8198; 2. An 
with
[type=“提交”]
attribute/value will of &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8195;course exhibit the same behavior as
. &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; 3. If the user is focused on certain form controls and hits the &#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198;&#8198; &#8195;<kbd>Enter</kbd>/<kbd>Return</kbd> key the 
“提交”`事件也被触发。

const msg = {
  user: `Min: 2 letters OR 1 letter,
         \u2003\u2003\u20091 space, and 1 letter.
         Max: 30 letters OR 1 space 
         \u2003\u2003\u2009and 29 letters.`,
  mail: `Ex: [email protected]`,
  cell: `Ex 123-123-1234 OR 123 123-1234`
};

const ui = document.forms.ui;

const validate = (e) => {
  const tag = e.target;
  if (tag.matches("input")) {
    tag.setCustomValidity(msg[tag.id]);
    const rgx = new RegExp(tag.pattern);
    if (!rgx.test(tag.value)) {
      tag.classList.add("invalid");
      tag.classList.remove("valid");
      tag.validationMessage
    } else {
      tag.classList.add("valid");
      tag.classList.remove("invalid");
      tag.setCustomValidity("");
    }
    if (tag.value.length < 1) {
      tag.classList.remove("valid");
      tag.classList.remove("invalid");
      tag.setCustomValidity("");
    }
  }
};

ui.addEventListener("input", validate);
:root {
  font: 2ch/1.2 "Segoe UI"
}

form {
  width: min-content;
}

fieldset {
  width: max-content;
  margin: 0;
}

legend {
  font-size: 1.15rem
}

label {
  display: inline-block;
  width: 2.25rem;
}

input,
button {
  padding: 4px;
  font: inherit;
}

input {
  margin: 0.5rem 0 0;
}

output {
  display: inline-block;
}

input+output::before {
  content: "\0000a0\0000a0\0000a0\0000a0\0000a0\0000a0";
}

input.valid {
  outline: green solid 2px;
}

input.valid+output::before {
  content: "\002713";
  display: inline-block;
  margin: 0 0.25rem;
  font-size: 1.60rem;
  line-height: 0;
  color: green;
  translate: 0 0.25rem;
}

input.invalid {
  outline: red solid 2px
}

input.invalid+output::before {
  content: "\00274c";
}

button {
  float: right;
  margin: 0.5rem 1.75rem 0 0;
  cursor: pointer;
}

[type="reset"] {
  margin-right: 0.25rem;
}

iframe {
  width: 18rem;
}
<form id="ui" action="https://httpbin.org/post" method="POST" target="response">
  <fieldset>
    <legend>Contact Info</legend>
    <label for="user">User</label>
    <input id="user" name="user" pattern="^[a-z]+\s*?[a-z]+$" maxlength="30" required>
    <output for="user" name="error"></output><br>

    <label for="mail">Mail</label>
    <input id="mail" name="mail" pattern="[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$" type="email" required>
    <output for="user" name="error"></output><br>

    <label for="cell">Cell</label>
    <input id="cell" name="cell" pattern="^\d{3}(?:\s|-)\d{3}-\d{4}$" maxlength="12" type="tel" required>
    <output for="user" name="error"></output><br>

    <button>Send</button>
    <button type="reset">Clear</button>
  </fieldset>
</form>

<iframe name="response"></iframe>

© www.soinside.com 2019 - 2024. All rights reserved.