Home > Net >  I am attempting to refactor my JS, for CSS/JS form validation
I am attempting to refactor my JS, for CSS/JS form validation

Time:10-01

I am struggling to figure out how to refactor my JS for form validation. I figured out how to target each element using there ID, I just feel there has to be a better way to clean up this code using an array though. Any help would be appreciated. The commented out JS was my attempt at beginning to refactor.

"use strict"

//clunky code for error-img:
const fNameInput = document.getElementById("first-input");
const lNameInput = document.getElementById("last-input");
const emailInput = document.getElementById("email");
const passwordInput = document.getElementById("password");

fNameInput.addEventListener("blur", function(){
    const errorImg1 = document.getElementById("error-img1");
    
    if(!fNameInput.value){
        errorImg1.classList.add("visible");
    } else{
        errorImg1.classList.remove("visible");
    }
});

lNameInput.addEventListener("blur", function(){
    const errorImg2 = document.getElementById("error-img2");

    if(!lNameInput.value){
        errorImg2.classList.add("visible");
    } else{
        errorImg2.classList.remove("visible");
    }
});

emailInput.addEventListener("blur", function(){
    const errorImg3 = document.getElementById("error-img3");

    if(!emailInput.value){
        errorImg3.classList.add("visible");
    } else{
        errorImg3.classList.remove("visible");
    }
});

passwordInput.addEventListener("blur", function(){
    const errorImg4 = document.getElementById("error-img4");

    if(!passwordInput.value){
        errorImg4.classList.add("visible");
    } else{
        errorImg4.classList.remove("visible");
    }
});


// attempt at making clean code that applies to all inputs:

// const inputs = document.getElementsByTagName("input");
// const errorImgs = document.getElementsByClassName("error-img");

// //learn forEach loop:

// inputs[i].addEventListener("blur", () => {
//     inputs.forEach(element => {
//         if(!element.value){

//         }
//     });
// });
@import url(https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap);

body{
    margin: 0;
    background-image: url(images/bg-intro-desktop.png);
    background-color: rgba(255, 0, 0, .5);
    font-family: 'Poppins', sans-serif;
}

.container{
    margin: 6rem auto;
    height: auto;
    width: 70%;
    
    display: grid;
    grid-template-areas: 
    "left right"
    "left right";
    grid-auto-columns: minmax(0, 32rem);
}

.left-container{
    grid-area: left;
}

.left-heading{
    color: white;
    font-size: 2.7rem;
    line-height: 3rem;
    font-weight: 700;

    position: relative;
    top: 30%;
}

.left-content{
    color: white;
    font-size: .9rem;
    font-weight: 400;

    position: relative;
    top: 29%;
}

.right-container{
    grid-area: right;
}

.right-top-container{
    background-color: hsl(248, 32%, 49%);
    border-radius: .5rem;
    height: 11.5%;
    margin: auto auto 1.5rem auto;
    box-shadow: 0px 6px 1px rgba(0, 0, 0, .2);
}

.top-text-special{
    color: white;
    font-weight: 600;
    font-size: .85rem;

    position: relative;
    top: 50%;
    left: 72%;
    transform: translate(-50%, -50%);
}

.top-text{
    color: white;
    font-weight: 400;
}

.right-form{
    background-color: white;
    height: 23rem;
    border-radius: .5rem;
    padding: 1.5rem;
    padding-top: 2.5rem;
    box-shadow: 0 7px 1px rgba(0, 0, 0, .15);

    display: grid;
    grid-template-areas: 
    "fname"
    "lname"
    "email"
    "password"
    "submit-btn"
    "bottom-text";
}

input{
    font-family: 'Poppins', sans-serif;
    font-weight: 500;
    font-size: .8rem;

    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 0 0 0 1.5rem;

    height: 3rem;
    width: 90%;

    border: solid;
    border-width: .09rem;
    border-radius: .4rem;
    border-color: hsl(246, 25%, 77%);
}

.first-name-div{
    grid-area: fname;
    position: relative;
}

.last-name-div{
    grid-area: lname;
    position: relative;
}

.email-div{
    grid-area: email;
    position: relative;
}

.password-div{
    grid-area: password;
    position: relative;
}

.submit-btn-div{
    grid-area: submit-btn;
}

#submit-btn{
    position: relative;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 97.1%;
    height: 3rem;
    border-radius: .4rem;

    background-color: hsl(154, 59%, 51%);
    border: none;
    color: white;
    box-shadow: 0 2px .5px rgba(0, 0, 0, .4);

    cursor: pointer;
}

.right-footer-text{
    grid-area: bottom-text;
}

.footer-text{
    position: relative;
    top: 1%;
    left: 58%;
    transform: translate(-50%, -50%);

    color: grey;
    font-size: .7rem;
    font-weight: 600;
}

.footer-span{
    color: hsl(0, 100%, 74%);
}

.error-img{
    display: none;
    position: absolute;
    bottom: 75%;
    right: 5%;
}

.visible{
    display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FrontEndMentor#3</title>
    <link rel="stylesheet" href="styles.css">
    <link rel="shortcut icon" href="images/favicon-32x32.png" type="image/x-icon">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">

</head>
<body>
    <div >
        <div >
            <h1 >Learn to code by <br>
                 watching others</h1>
            <p >See how experienced developers solve problems in real-time. <br>
                Watching scripted tutorials is great, but understanding how <br>
                developers think is invaluable.</p>
        </div>

        <div >
            <div >
                <p >Try it free 7 days <span >then $20/mo. thereafter</span></p>
            </div>


            <div >
            <form action="#" >
                <div >
                    <input type="text" name="fname" id="first-input" placeholder="First Name">
                    <span><img  id="error-img1" src="images/icon-error.svg" alt=""></span>
                </div>
                <div >
                    <input type="text" name="lname" id="last-input" placeholder="Last Name">
                    <span><img  id="error-img2" src="images/icon-error.svg" alt=""></span>
                </div>
                <div >
                    <input type="email" name="email-address" id="email" placeholder="[email protected]">
                    <span><img  id="error-img3" src="images/icon-error.svg" alt=""></span>
                </div>
                <div >
                    <input type="password" name="password" id="password" placeholder="Password">
                    <span><img  id="error-img4" src="images/icon-error.svg" alt=""></span>
                </div>
                <div >
                    <button type="submit" id="submit-btn">CLAIM YOUR FREE TRIAL</button>
                </div>
                </form>
            </div>


            <div >
                <p >By clicking the button, you are agreeing to our <span >Terms and Services</span></p>
            </div>
        </div>
    </div>
    <script src="index.js"></script>
</body>
</html>

(Not all styling is complete, will do when JS is cleaned up)

CodePudding user response:

To improve your JS logic you can apply the 'Don't Repeat Yourself' principle, or DRY. This means that you should avoid code repetition by genericising the logic as much as possible. The corollary effect of this is that the code can be made to work for an infinite number of form fields, so long as the HTML structure around them is consistent.

Here's an example of this for your use case. Note the use of a single class applied to all the fields to be validated, and also how DOM traversal is used to find the error img related to the current field only.

"use strict"

const fields = document.querySelectorAll('.to-validate').forEach(el => {
  el.addEventListener('blur', e => {
    let field = e.target;
    field.closest('div').querySelector('.error-img').classList.toggle('visible', !field.value.trim().length);
  });
});
@import url(https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap);
body {
  margin: 0;
  background-image: url(images/bg-intro-desktop.png);
  background-color: rgba(255, 0, 0, .5);
  font-family: 'Poppins', sans-serif;
}

.container {
  margin: 6rem auto;
  height: auto;
  width: 70%;
  display: grid;
  grid-template-areas: "left right" "left right";
  grid-auto-columns: minmax(0, 32rem);
}

.left-container {
  grid-area: left;
}

.left-heading {
  color: white;
  font-size: 2.7rem;
  line-height: 3rem;
  font-weight: 700;
  position: relative;
  top: 30%;
}

.left-content {
  color: white;
  font-size: .9rem;
  font-weight: 400;
  position: relative;
  top: 29%;
}

.right-container {
  grid-area: right;
}

.right-top-container {
  background-color: hsl(248, 32%, 49%);
  border-radius: .5rem;
  height: 11.5%;
  margin: auto auto 1.5rem auto;
  box-shadow: 0px 6px 1px rgba(0, 0, 0, .2);
}

.top-text-special {
  color: white;
  font-weight: 600;
  font-size: .85rem;
  position: relative;
  top: 50%;
  left: 72%;
  transform: translate(-50%, -50%);
}

.top-text {
  color: white;
  font-weight: 400;
}

.right-form {
  background-color: white;
  height: 23rem;
  border-radius: .5rem;
  padding: 1.5rem;
  padding-top: 2.5rem;
  box-shadow: 0 7px 1px rgba(0, 0, 0, .15);
  display: grid;
  grid-template-areas: "fname" "lname" "email" "password" "submit-btn" "bottom-text";
}

input {
  font-family: 'Poppins', sans-serif;
  font-weight: 500;
  font-size: .8rem;
  position: relative;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 0 0 0 1.5rem;
  height: 3rem;
  width: 90%;
  border: solid;
  border-width: .09rem;
  border-radius: .4rem;
  border-color: hsl(246, 25%, 77%);
}

.first-name-div {
  grid-area: fname;
  position: relative;
}

.last-name-div {
  grid-area: lname;
  position: relative;
}

.email-div {
  grid-area: email;
  position: relative;
}

.password-div {
  grid-area: password;
  position: relative;
}

.submit-btn-div {
  grid-area: submit-btn;
}

#submit-btn {
  position: relative;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 97.1%;
  height: 3rem;
  border-radius: .4rem;
  background-color: hsl(154, 59%, 51%);
  border: none;
  color: white;
  box-shadow: 0 2px .5px rgba(0, 0, 0, .4);
  cursor: pointer;
}

.right-footer-text {
  grid-area: bottom-text;
}

.footer-text {
  position: relative;
  top: 1%;
  left: 58%;
  transform: translate(-50%, -50%);
  color: grey;
  font-size: .7rem;
  font-weight: 600;
}

.footer-span {
  color: hsl(0, 100%, 74%);
}

.error-img {
  display: none;
  position: absolute;
  bottom: 75%;
  right: 5%;
  /* following rules only for this demo */
  width: 20px;
  height: 20px;
  background-color: #C00;
}

.visible {
  display: block;
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">

<div >
  <div >
    <h1 >Learn to code by <br> watching others</h1>
    <p >See how experienced developers solve problems in real-time. <br> Watching scripted tutorials is great, but understanding how <br> developers think is invaluable.</p>
  </div>
  <div >
    <div >
      <p >Try it free 7 days <span >then $20/mo. thereafter</span></p>
    </div>
    <div >
      <form action="#" >
        <div >
          <input type="text" name="fname" id="first-input" placeholder="First Name"  />
          <span><img  src="images/icon-error.svg" alt=""></span>
        </div>
        <div >
          <input type="text" name="lname" id="last-input" placeholder="Last Name"  />
          <span><img  src="images/icon-error.svg" alt=""></span>
        </div>
        <div >
          <input type="email" name="email-address" id="email" placeholder="[email protected]"  />
          <span><img  src="images/icon-error.svg" alt=""></span>
        </div>
        <div >
          <input type="password" name="password" id="password" placeholder="Password"  />
          <span><img  src="images/icon-error.svg" alt=""></span>
        </div>
        <div >
          <button type="submit" id="submit-btn">CLAIM YOUR FREE TRIAL</button>
        </div>
      </form>
    </div>
    <div >
      <p >By clicking the button, you are agreeing to our <span >Terms and Services</span></p>
    </div>
  </div>
</div>

It's worth noting, though, that what you're doing can be achieved simply by adding the required attribute to your input elements. Then there's no JS required at all.

CodePudding user response:

You can do this with required attribute and css invalid selector. No need to code anything. But by default the error will show up.

input   span{
  display: none;
  color: red;
}

input:invalid   span{
  display: inline-block;
}
<form action="#" >
  <div >
    <input type="text" name="fname" id="first-input" placeholder="First Name" required>
    <span>X</span>
  </div>
  <div >
    <input type="text" name="lname" id="last-input" placeholder="Last Name" required>
    <span>X</span>
  </div>
  <div >
    <input type="email" name="email-address" id="email" placeholder="[email protected]" required>
    <span>X</span>
  </div>
  <div >
    <input type="password" name="password" id="password" placeholder="Password" required>
    <span>X</span>
  </div>
  <div >
    <button type="submit" id="submit-btn">CLAIM YOUR FREE TRIAL</button>
  </div>
</form>

Now if you want it to show up, you would have to do what you are doing with blur and add a class it it was interacted with. Using foucsout with event delegation you just have to add one event listener to the form.

document.querySelector("form").addEventListener("focusout", function (e) {
  const input = e.target.closest('input');
  if (input) input.classList.add('touched');
});
input   span{
  display: none;
  color: red;
}

input.touched:invalid   span{
  display: inline-block;
}
<form action="#" >
  <div >
    <input type="text" name="fname" id="first-input" placeholder="First Name" required>
    <span>X</span>
  </div>
  <div >
    <input type="text" name="lname" id="last-input" placeholder="Last Name" required>
    <span>X</span>
  </div>
  <div >
    <input type="email" name="email-address" id="email" placeholder="[email protected]" required>
    <span>X</span>
  </div>
  <div >
    <input type="password" name="password" id="password" placeholder="Password" required>
    <span>X</span>
  </div>
  <div >
    <button type="submit" id="submit-btn">CLAIM YOUR FREE TRIAL</button>
  </div>
</form>

  • Related