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>