Really struggling with specific form validation

Hi

I am building a simple Form with name, email and card number as inputs, however I cannot get the Email & Card inputs to validate in a specific way, no matter what I try.

I need the email input to validate for valid emails only, however it does not allow for any numbers or special characters such as ._- to be entered so it only allows letters strictly for the email input.

I need the card input to only allow 16 digit numbers that conform to the Luhn algorithm, however the input only validates for 16 digits and does not validate for numbers that conform to the Luhn algorithm. I am wondering what I am doing wrong, as I cannot figure out what I am doing wrong. I have pasted my code below.

Instant Validation Form .error { color: red; }
    .valid {
        border: 2px solid green;
    }

    .invalid {
        border: 2px solid red;
    }
</style>

Instant Validation Form

Name:

    <label for="email">Email:</label>
    <input type="email" id="email" required><br>
    <span class="error" id="emailError"></span><br>

    <label for="cardNumber">Card Number:</label>
    <input type="text" id="cardNumber" pattern="[0-9]{16}" required><br>
    <span class="error" id="cardNumberError"></span><br>

    <input type="submit" value="Submit">
</form>

<script>
    const form = document.getElementById("validationForm");
    const nameInput = document.getElementById("name");
    const emailInput = document.getElementById("email");
    const cardNumberInput = document.getElementById("cardNumber");

    function validateInputs() {
        const nameRegex = /^[A-Za-z]+$/;
        const emailRegex = /^[A-Za-z!#$%&'*+\-/=?^_`{|}~]+@[A-Za-z]+\.[A-Za-z]{2,}$/;
        const cardNumberRegex = /^[0-9]{16}$/;

        const isNameValid = nameRegex.test(nameInput.value);
        const isEmailValid = emailRegex.test(emailInput.value);
        const isCardNumberValid = cardNumberRegex.test(cardNumberInput.value);

        if (isNameValid) {
            nameInput.classList.remove("invalid");
            nameInput.classList.add("valid");
            document.getElementById("nameError").textContent = "";
        } else {
            nameInput.classList.remove("valid");
            nameInput.classList.add("invalid");
            document.getElementById("nameError").textContent = "Invalid name. Should contain at least one letter and no special characters.";
        }

        if (isEmailValid) {
            emailInput.classList.remove("invalid");
            emailInput.classList.add("valid");
            document.getElementById("emailError").textContent = "";
        } else {
            emailInput.classList.remove("valid");
            emailInput.classList.add("invalid");
            document.getElementById("emailError").textContent = "Invalid email address.";
        }

        if (isCardNumberValid) {
            cardNumberInput.classList.remove("invalid");
            cardNumberInput.classList.add("valid");
            document.getElementById("cardNumberError").textContent = "";
        } else {
            cardNumberInput.classList.remove("valid");
            cardNumberInput.classList.add("invalid");
            document.getElementById("cardNumberError").textContent = "Invalid card number. Must be 16 digits.";
        }

        return isNameValid && isEmailValid && isCardNumberValid;
    }

    form.addEventListener("submit", function (event) {
        if (!validateInputs()) {
            event.preventDefault();
        }
    });

    // Validate inputs on input change
    nameInput.addEventListener("input", validateInputs);
    emailInput.addEventListener("input", validateInputs);
    cardNumberInput.addEventListener("input", validateInputs);
</script>

So with Luhn, you can’t use regex: regex is for text patterns. The Luhn alhorithm is applied to a set of digits. You can check at a basic level (are there the correct number of digit characters?), but you need to run the Luhn algorithm on those digits.

So what you’re doing is…fine…but you need to actually write a function to implement a luhn check on the credit card input.

There are a few other things here I think you also want to fix. You are essentially re-implementing functionality that already exists. So:

The regex patterns can be moved to the pattern attribute in the HTML. This will enable native validation that matches your requirements. You also don’t need to check length for the card number, there are min and max length attributes you can add to the input.

Custom validation is also fully supported, you don’t need to manually add and remove “valid” attributes, all of this comes out of the box.

So what you want:

  • user types something
  • you validate that (either as they type or once they’re finished etc)
  • if there’s an error show a custom message & apply the error styling
  • if not, no problem, don’t show the error
  • HTML supports regex patterns out of the box
  • HTML does not support more complex validation required for luhn check

You aren’t changing the error message based on input, so there’s no point injecting it based on the error: you can literally have it in the HTML & show/hide it.

CSS allows you to show/hide it because the valid and invalid pseudoclasses are added automatically. So something like:

input + .error {
  display: none;
}

input:invalid + .error {
  display: block;
}

Then to add validation based on the luhn check (I’ve added it on keyup here so it validates as a user types, but whatever seems appropriate):

cardNumber.addEventListener("keyup", (e) => {
  if (CARD_NUMBER_IS_VALID(e.target.value)) {
    cardNumber.customValidation("");
  } else {
    cardNumber.customValidation("Invalid card number");
  }
});

Where CARD_NUMBER_IS_VALID is a function that takes whatever the input is and says true or false.

As long as the string passed to customValidation is empty, the element will show as valid.

And if you have a more general error message (like “credit card number is incorrect” or something) then can still just show/hide.

You can also go nuts with custom validations and messages if you want, anyway the API you want is:

Then can’t remember the sequence of events here: if I’m remembering this correctly forms don’t validate on submit, so you’re right with what you’ve done to prevent default behaviour. However, that’s only necessary because you’ve reimplemented existing HTML validation: as long as it’s a live checking process (ie as the user types, which is what HTML form validation is) you could just disable the submit button if the form is invalid, like:

#validationForm:invalid input[type="submit"] {
  disabled: true;
}

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.