Build a Customer Complaint Form - Build a Customer Complaint Form

build a customer complaint form vallidation errors

hi totally blind, using the jaws screen reader 2025, nvda 2025, and windows narrator. not able to see the code. well have built the html, css and javascript. it is not passing, and did look at some sample code i found online from another student or person to get an idea how to do this. well have then googled and tried other versions. so some of the basic vallidation passes. and also did try this in my local copy of firefox and in the browser preview on the site. but when i run the tests, not passing all of them. so what am i doing wrong? how to get it to pass. have spent the last 2 to 3 days and have tried, and then rewritten the code a couple of times, js, have tested and tried to get this to pass. so asking for your help. any documentation or sample code samples to see what i am doing wrong. will paste the error list below. pasting now:

<!DOCTYPE html> <html lang="en"> <head>   <meta charset="UTF-8">   <meta name="viewport" content="width=device-width, initial-scale=1.0">   <title>Customer Complaint Form</title>   <link rel="stylesheet" href="styles.css"> </head> <body>   <header>     <h1>Customer Complaint Form</h1>   </header>    <main>     <form id="customer-form" novalidate>       <div class="form-group">         <label for="full-name">Full Name:</label>         <input type="text" id="full-name" name="full-name" required>         <span class="error-message" aria-live="polite"></span>       </div>        <div class="form-group">         <label for="email">Email:</label>         <input type="email" id="email" name="email" required>         <span class="error-message" aria-live="polite"></span>       </div>        <div class="form-group">         <label for="complaint">Complaint:</label>         <textarea id="complaint" name="complaint" required></textarea>         <span class="error-message" aria-live="polite"></span>       </div>        <div class="form-group">         <label for="priority">Priority:</label>         <select id="priority" name="priority" required>           <option value="">Select priority</option>           <option value="low">Low</option>           <option value="medium">Medium</option>           <option value="high">High</option>         </select>         <span class="error-message" aria-live="polite"></span>       </div>        <button type="submit">Submit Complaint</button>     </form>      <div id="form-status" aria-live="polite"></div>   </main>    <script src="script.js"></script> </body> </html> 
body {

  font-family: Arial, sans-serif;

  line-height: 1.5;

  margin: 0;

  padding: 0;

  background-color: #f5f5f5;

}



header {

  background-color: #0077cc;

  color: #fff;

  padding: 1rem;

  text-align: center;

}



main {

  max-width: 600px;

  margin: 2rem auto;

  background-color: #fff;

  padding: 2rem;

  border-radius: 10px;

  box-shadow: 0 0 10px rgba(0,0,0,0.1);

}



.form-group {

  margin-bottom: 1.5rem;

}



label {

  display: block;

  margin-bottom: 0.5rem;

  font-weight: bold;

}



input, textarea, select {

  width: 100%;

  padding: 0.5rem;

  border-radius: 5px;

  border: 1px solid #ccc;

  font-size: 1rem;

}



textarea {

  min-height: 100px;

}



button {

  background-color: #0077cc;

  color: #fff;

  border: none;

  padding: 0.75rem 1.5rem;

  font-size: 1rem;

  border-radius: 5px;

  cursor: pointer;

}



button:hover {

  background-color: #005fa3;

}



.error-message {

  color: red;

  font-size: 0.9rem;

  margin-top: 0.25rem;

  display: block;

}




#form-status {

  margin-top: 1rem;

  font-weight: bold;

  color: green;

}

Java Script:

// Helper: set border color
function setBorderColor(el, valid) {
  if (!el) return;
  el.style.borderColor = valid ? "green" : "red";
}

// Global function: validateForm
function validateForm() {
  const form = document.getElementById("customer-form");

  const validation = {
    "full-name": false,
    "email": false,
    "order-no": false,
    "product-code": false,
    "quantity": false,
    "complaints-group": false,
    "complaint-description": false,
    "solutions-group": false,
    "solution-description": false
  };

  // Full Name
  const fullName = form["full-name"].value.trim();
  validation["full-name"] = fullName !== "";
  setBorderColor(form["full-name"], validation["full-name"]);

  // Email
  const email = form["email"].value.trim();
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  validation["email"] = emailPattern.test(email);
  setBorderColor(form["email"], validation["email"]);

  // Order Number
  const orderNo = form["order-no"].value.trim();
  validation["order-no"] = orderNo !== "";
  setBorderColor(form["order-no"], validation["order-no"]);

  // Product Code
  const productCode = form["product-code"].value.trim();
  validation["product-code"] = productCode !== "";
  setBorderColor(form["product-code"], validation["product-code"]);

  // Quantity
  const quantity = form["quantity"].value.trim();
  validation["quantity"] = quantity !== "" && Number(quantity) > 0;
  setBorderColor(form["quantity"], validation["quantity"]);

  // Complaints group (checkboxes)
  const complaints = form.querySelectorAll("input[name='complaints']");
  validation["complaints-group"] = Array.from(complaints).some(c => c.checked);
  setBorderColor(document.getElementById("complaints-group"), validation["complaints-group"]);

  // Complaint description (Other)
  const otherComplaint = document.getElementById("other-complaint");
  const complaintDesc = document.getElementById("complaint-description");
  if (otherComplaint.checked) {
    validation["complaint-description"] = complaintDesc.value.trim().length >= 20;
    setBorderColor(complaintDesc, validation["complaint-description"]);
  } else {
    validation["complaint-description"] = true;
    complaintDesc.style.borderColor = "";
  }

  // Solutions group (radio)
  const solutions = form.querySelectorAll("input[name='solutions']");
  validation["solutions-group"] = Array.from(solutions).some(r => r.checked);
  setBorderColor(document.getElementById("solutions-group"), validation["solutions-group"]);

  // Solution description (Other)
  const otherSolution = document.getElementById("other-solution");
  const solutionDesc = document.getElementById("solution-description");
  if (otherSolution.checked) {
    validation["solution-description"] = solutionDesc.value.trim().length >= 20;
    setBorderColor(solutionDesc, validation["solution-description"]);
  } else {
    validation["solution-description"] = true;
    solutionDesc.style.borderColor = "";
  }

  return validation;
}

// Global function: isValid
function isValid(validationObj) {
  return Object.values(validationObj).every(v => v === true);
}

// Add change/input listeners for live validation
function addChangeListeners() {
  const form = document.getElementById("customer-form");

  const fields = [
    "full-name",
    "email",
    "order-no",
    "product-code",
    "quantity",
    "complaint-description",
    "solution-description"
  ];

  fields.forEach(id => {
    const el = document.getElementById(id);
    if (el) {
      el.addEventListener("input", validateForm);
      el.addEventListener("change", validateForm);
    }
  });

  const complaints = form.querySelectorAll("input[name='complaints']");
  complaints.forEach(c => c.addEventListener("change", validateForm));

  const solutions = form.querySelectorAll("input[name='solutions']");
  solutions.forEach(r => r.addEventListener("change", validateForm));
}

// Initialize
document.addEventListener("DOMContentLoaded", () => {
  addChangeListeners();

  const form = document.getElementById("customer-form");
  const statusDiv = document.getElementById("form-status");

  form.addEventListener("submit", (e) => {
    e.preventDefault();
    const validation = validateForm();
    if (isValid(validation)) {
      statusDiv.textContent = "Form submitted successfully!";
      form.reset();

      // Reset borders
      Array.from(form.elements).forEach(el => el.style.borderColor = "");
      document.getElementById("complaints-group").style.borderColor = "";
      document.getElementById("solutions-group").style.borderColor = "";
    } else {
      statusDiv.textContent = "";
    }
  });
});

Project :

https://www.freecodecamp.org/learn/full-stack-developer/lab-customer-complaint-form/build-a-customer-complaint-form

so can you help me out. totally blind. and using windows 11 pro 2024. using firefox as my primary browser. i do have chrome and edge and tested it as well. does do all the relevant tests like:
vallidation errors if blank fields. and the submit button.
if all fields are entere then success message, as i cannot see and dont have the aria accessibility to speak out vallidation errors if this was a real world production app or page and not a . so can you help.
thank you.
Marvin.learning environemtn.Tell us what’s happening:

hi doing the customer complaint form, not passing and blind and using a screen reader.

Your code so far

html

<!-- file: index.html -->

css

/* file: styles.css */

js

/* file: script.js */

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:143.0) Gecko/20100101 Firefox/143.0

Challenge Information:

Build a Customer Complaint Form - Build a Customer Complaint Form

https://www.freecodecamp.org/learn/full-stack-developer/lab-customer-complaint-form/build-a-customer-complaint-form

hi, can any one help me out with this project. been waiting over 15 hours. so can any one help? patiently waiting.

thank you.

marvin.

1 Like

Hi @BlindVisionMan

I noticed your HTML and CSS are different to the code supplied in the lab.

For the JavaScript, I noticed there is an error message in the console.

Uncaught TypeError: Cannot read properties of null (reading ‘querySelectorAll’)

Check the syntax for the selector.

Happy coding

hi. okay tried modifying the code. but still getting errors so will post my html, css and javascript code and also the errors. so is this a bug or just my code. please advice.

pasting below.

html:

RESET PASSWORD css: body {

font-family: Arial, sans-serif;

line-height: 1.5;

margin: 0;

padding: 0;

background-color: #f5f5f5;

}

header {

background-color: #0077cc;

color: #fff;

padding: 1rem;

text-align: center;

}

main {

max-width: 600px;

margin: 2rem auto;

background-color: #fff;

padding: 2rem;

border-radius: 10px;

box-shadow: 0 0 10px rgba(0,0,0,0.1);

}

.form-group {

margin-bottom: 1.5rem;

}

label {

display: block;

margin-bottom: 0.5rem;

font-weight: bold;

}

input, textarea, select {

width: 100%;

padding: 0.5rem;

border-radius: 5px;

border: 1px solid #ccc;

font-size: 1rem;

}

textarea {

min-height: 100px;

}

button {

background-color: #0077cc;

color: #fff;

border: none;

padding: 0.75rem 1.5rem;

font-size: 1rem;

border-radius: 5px;

cursor: pointer;

}

button:hover {

background-color: #005fa3;

}

.error-message {

color: red;

font-size: 0.9rem;

margin-top: 0.25rem;

display: block;

}

#form-status {

margin-top: 1rem;

font-weight: bold;

color: green;

}

javascript:

// Helper: set border color
function setBorderColor(el, valid) {
  if (!el) return;
  el.style.borderColor = valid ? "green" : "red";
}

// Validate form function
function validateForm() {
  const form = document.getElementById("customer-form");
  const result = {};

  // Full name
  const fullNameEl = form["full-name"];
  const fullName = fullNameEl.value.trim();
  result["full-name"] = fullName !== "";

  // Email
  const emailEl = form["email"];
  const email = emailEl.value.trim();
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  result["email"] = emailPattern.test(email);

  // Order number
  const orderNoEl = form["order-no"];
  const orderNo = orderNoEl.value.trim();
  result["order-no"] = orderNo !== "";

  // Product code
  const productCodeEl = form["product-code"];
  const productCode = productCodeEl.value.trim();
  result["product-code"] = productCode !== "";

  // Quantity
  const quantityEl = form["quantity"];
  const quantity = quantityEl.value.trim();
  result["quantity"] = quantity !== "" && Number(quantity) > 0;

  // Complaints group
  const complaints = form.querySelectorAll("input[name='complaints']");
  result["complaints-group"] = Array.from(complaints).some(c => c.checked);

  // Complaint description
  const otherComplaintEl = document.getElementById("other-complaint");
  const complaintDescEl = document.getElementById("complaint-description");
  if (otherComplaintEl.checked) {
    result["complaint-description"] = complaintDescEl.value.trim().length >= 20;
  } else {
    result["complaint-description"] = true;
  }

  // Solutions group
  const solutions = form.querySelectorAll("input[name='solutions']");
  result["solutions-group"] = Array.from(solutions).some(r => r.checked);

  // Solution description
  const otherSolutionEl = document.getElementById("other-solution");
  const solutionDescEl = document.getElementById("solution-description");
  if (otherSolutionEl.checked) {
    result["solution-description"] = solutionDescEl.value.trim().length >= 20;
  } else {
    result["solution-description"] = true;
  }

  return result;
}

// isValid function
function isValid(validationObj) {
  return Object.values(validationObj).every(v => v === true);
}

// Add per-field event listeners
function addListeners() {
  const form = document.getElementById("customer-form");

  // Full name
  const fullNameEl = form["full-name"];
  fullNameEl.addEventListener("change", () => {
    setBorderColor(fullNameEl, fullNameEl.value.trim() !== "");
  });

  // Email
  const emailEl = form["email"];
  emailEl.addEventListener("change", () => {
    const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    setBorderColor(emailEl, emailPattern.test(emailEl.value.trim()));
  });

  // Order number
  const orderNoEl = form["order-no"];
  orderNoEl.addEventListener("change", () => {
    setBorderColor(orderNoEl, orderNoEl.value.trim() !== "");
  });

  // Product code
  const productCodeEl = form["product-code"];
  productCodeEl.addEventListener("change", () => {
    setBorderColor(productCodeEl, productCodeEl.value.trim() !== "");
  });

  // Quantity
  const quantityEl = form["quantity"];
  quantityEl.addEventListener("change", () => {
    const valid = quantityEl.value.trim() !== "" && Number(quantityEl.value) > 0;
    setBorderColor(quantityEl, valid);
  });

  // Complaints group
  const complaints = form.querySelectorAll("input[name='complaints']");
  complaints.forEach(c => {
    c.addEventListener("change", () => {
      const anyChecked = Array.from(complaints).some(cb => cb.checked);
      setBorderColor(document.getElementById("complaints-group"), anyChecked);
    });
  });

  // Other complaint description
  const otherComplaintEl = document.getElementById("other-complaint");
  const complaintDescEl = document.getElementById("complaint-description");
  otherComplaintEl.addEventListener("change", () => {
    if (otherComplaintEl.checked) {
      setBorderColor(complaintDescEl, complaintDescEl.value.trim().length >= 20);
    } else {
      complaintDescEl.style.borderColor = "";
    }
  });
  complaintDescEl.addEventListener("input", () => {
    if (otherComplaintEl.checked) {
      setBorderColor(complaintDescEl, complaintDescEl.value.trim().length >= 20);
    }
  });

  // Solutions group
  const solutions = form.querySelectorAll("input[name='solutions']");
  solutions.forEach(r => {
    r.addEventListener("change", () => {
      const anyChecked = Array.from(solutions).some(rb => rb.checked);
      setBorderColor(document.getElementById("solutions-group"), anyChecked);
    });
  });

  // Other solution description
  const otherSolutionEl = document.getElementById("other-solution");
  const solutionDescEl = document.getElementById("solution-description");
  otherSolutionEl.addEventListener("change", () => {
    if (otherSolutionEl.checked) {
      setBorderColor(solutionDescEl, solutionDescEl.value.trim().length >= 20);
    } else {
      solutionDescEl.style.borderColor = "";
    }
  });
  solutionDescEl.addEventListener("input", () => {
    if (otherSolutionEl.checked) {
      setBorderColor(solutionDescEl, solutionDescEl.value.trim().length >= 20);
    }
  });
}

// Initialize form
document.addEventListener("DOMContentLoaded", () => {
  addListeners();

  const form = document.getElementById("customer-form");
  const statusDiv = document.getElementById("form-status");

  form.addEventListener("submit", e => {
    e.preventDefault();
    const validation = validateForm();
    // Update all border colors at submit
    const formElements = [
      "full-name","email","order-no","product-code","quantity",
      "complaints-group","complaint-description",
      "solutions-group","solution-description"
    ];
    formElements.forEach(id => {
      let el = document.getElementById(id) || form[id];
      if (!el) return;
      if (id.includes("group")) {
        setBorderColor(el, validation[id]);
      } else if (id.includes("description")) {
        setBorderColor(el, validation[id]);
      } else {
        setBorderColor(el, validation[id]);
      }
    });

    if (isValid(validation)) {
      statusDiv.textContent = "Form submitted successfully!";
      form.reset();
      // Reset all borders
      formElements.forEach(id => {
        let el = document.getElementById(id) || form[id];
        if (el) el.style.borderColor = "";
      });
    } else {
      statusDiv.textContent = "";
    }
  });
});

errors:
Passed:1. You should have a function named validateForm.
Passed:2. validateForm should return an object.
Passed:3. validateForm()["full-name"] should be false when #full-name is empty, and true otherwise.
Passed:4. When a change event is triggered on #full-name, you should set its border color to green if it contains a valid value, and red otherwise.
Passed:5. validateForm()["email"] should be true when #email contains a valid email address, and false otherwise.
Passed:6. When a change event is triggered on #email, you should set its border color to green if it contains a valid email address, and red otherwise.
Failed:7. validateForm()["order-no"] should be true when #order-no contains a valid value, and false otherwise.
Failed:8. When a change event is triggered on #order-no, you should set its border color to green if it contains a valid value, and red otherwise.
Failed:9. validateForm()["product-code"] should be true when #product-code contains a valid value, and false otherwise.
Failed:10. When a change event is triggered on #product-code, you should set its border color to green if it contains a valid value, and red otherwise.
Passed:11. validateForm()["quantity"] should be true when #quantity contains a valid value, and false otherwise.
Passed:12. When a change event is triggered on #quantity, you should set its border color to green if it contains a valid value, and red otherwise.
Failed:13. When at least one checkbox from #complaints-group is checked, validateForm()["complaints-group"] should be true.
Failed:14. When none of the checkboxes from #complaints-group is checked, validateForm()["complaints-group"] should be false.
Failed:15. Once one checkbox from #complaints-group is checked, you should set #complaints-group's border color to green.
Failed:16. When all of the checkboxes from #complaints-group are changed to the unchecked state, you should set #complaints-group's border color to red.
Passed:17. When #other-complaint is checked and #complaint-description contains at least twenty characters, validateForm()["complaint-description"] should
be true.
Passed:18. When #other-complaint is checked and #complaint-description contains less than twenty characters, validateForm()["complaint-description"] should
be false.
Passed:19. When #other-complaint is checked and the value of #complaint-description is changed to a valid value, you should set its border color to green.
Passed:20. When #other-complaint is checked and the value of #complaint-description is changed to an invalid value, you should set its border color to
red.
Failed:21. When a radio button from #solutions-group is checked, validateForm()["solutions-group"] should be true.
Failed:22. When none of the radio buttons from #solutions-group is checked, validateForm()["solutions-group"] should be false.
Failed:23. Once a radio button from #solutions-group is checked, you should set #solutions-group's border color to green.
Failed:24. When all of the radio buttons from #solutions-group are in the unchecked state after a change event, you should set #solutions-group's border
color to red.
Passed:25. When #other-solution is checked and #solution-description contains at least twenty characters, validateForm()["solution-description"] should
be true.
Passed:26. When #other-solution is checked and #solution-description contains less than twenty characters, validateForm()["solution-description"] should
be false.
Passed:27. When #other-solution is checked and the value of #solution-description is changed to a valid value, you should set its border color to green.
Passed:28. When #other-solution is checked and the value of #solution-description is changed to an invalid value, you should set its border color to red.
Passed:29. You should have a function named isValid.
Failed:30. Your isValid function should take the validateForm() as its argument and return true when all the form fields have been filled correctly.
Failed:31. Your isValid function should take the validateForm() as its argument and return false when not all the form fields have been filled correctly.
Failed:32. You should call isValid when you try to submit the form.
list end

Hi @BlindVisionMan

There is an error message in the console.

Uncaught TypeError: Cannot read properties of null (reading ‘full-name’)

Check the syntax for how to retrieve an HTML element using JavaScript.

Also, check how many times the fullNameEl variable is declared.

Happy coding

hi, okay so will paste the errors below, you have my current code. unless have to show you the new code. please advice, will do a test, and then if this is a bug with the fcc testing system or my code. please advice, been trying to get this to pass for the past week. so not passing. can any one help me out.

frustrated and using a screen reader.

marvin.

ps: will paste the errors below.

  • Passed:1. You should have a function named validateForm.

  • Passed:2. validateForm should return an object.

  • Passed:3. validateForm()["full-name"] should be false when #full-name is empty, and true otherwise.

  • Passed:4. When a change event is triggered on #full-name, you should set its border color to green if it contains a valid value, and red otherwise.

  • Passed:5. validateForm()["email"] should be true when #email contains a valid email address, and false otherwise.

  • Passed:6. When a change event is triggered on #email, you should set its border color to green if it contains a valid email address, and red otherwise.

  • Failed:7. validateForm()["order-no"] should be true when #order-no contains a valid value, and false otherwise.

  • Failed:8. When a change event is triggered on #order-no, you should set its border color to green if it contains a valid value, and red otherwise.

  • Failed:9. validateForm()["product-code"] should be true when #product-code contains a valid value, and false otherwise.

  • Failed:10. When a change event is triggered on #product-code, you should set its border color to green if it contains a valid value, and red otherwise.

  • Passed:11. validateForm()["quantity"] should be true when #quantity contains a valid value, and false otherwise.

  • Passed:12. When a change event is triggered on #quantity, you should set its border color to green if it contains a valid value, and red otherwise.

  • Failed:13. When at least one checkbox from #complaints-group is checked, validateForm()["complaints-group"] should be true.

  • Failed:14. When none of the checkboxes from #complaints-group is checked, validateForm()["complaints-group"] should be false.

  • Failed:15. Once one checkbox from #complaints-group is checked, you should set #complaints-group’s border color to green.

  • Failed:16. When all of the checkboxes from #complaints-group are changed to the unchecked state, you should set #complaints-group’s border color to red.

  • Passed:17. When #other-complaint is checked and #complaint-description contains at least twenty characters, validateForm()["complaint-description"] should be true.

  • Passed:18. When #other-complaint is checked and #complaint-description contains less than twenty characters, validateForm()["complaint-description"] should be false.

  • Passed:19. When #other-complaint is checked and the value of #complaint-description is changed to a valid value, you should set its border color to green.

  • Passed:20. When #other-complaint is checked and the value of #complaint-description is changed to an invalid value, you should set its border color to red.

  • Failed:21. When a radio button from #solutions-group is checked, validateForm()["solutions-group"] should be true.

  • Failed:22. When none of the radio buttons from #solutions-group is checked, validateForm()["solutions-group"] should be false.

  • Failed:23. Once a radio button from #solutions-group is checked, you should set #solutions-group’s border color to green.

  • Failed:24. When all of the radio buttons from #solutions-group are in the unchecked state after a change event, you should set #solutions-group’s border color to red.

  • Passed:25. When #other-solution is checked and #solution-description contains at least twenty characters, validateForm()["solution-description"] should be true.

  • Passed:26. When #other-solution is checked and #solution-description contains less than twenty characters, validateForm()["solution-description"] should be false.

  • Passed:27. When #other-solution is checked and the value of #solution-description is changed to a valid value, you should set its border color to green.

  • Passed:28. When #other-solution is checked and the value of #solution-description is changed to an invalid value, you should set its border color to red.

  • Passed:29. You should have a function named isValid.

  • Failed:30. Your isValid function should take the validateForm() as its argument and return true when all the form fields have been filled correctly.

  • Failed:31. Your isValid function should take the validateForm() as its argument and return false when not all the form fields have been filled correctly.

  • Failed:32. You should call isValid when you try to submit the form.

Hi @BlindVisionMan

Please post your updated JavaScript code.

Happy coding

hi, heres the latest js script from the fcc editor. not my own copy of vs code. so pasting below. if it is my code, then maybe point me to some documentation or sample code where i can see to get it to pass. or maybe test on the fcc. frustrated.

marvin.

ps: pasting below.

// Helper: set border color
function setBorderColor(el, valid) {
  if (!el) return;
  el.style.borderColor = valid ? "green" : "red";
}

// Validate form function
function validateForm() {
  const result = {};

  // Full Name
  const fullNameEl = document.getElementById("full-name");
  result["full-name"] = fullNameEl.value.trim() !== "";

  // Email
  const emailEl = document.getElementById("email");
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  result["email"] = emailPattern.test(emailEl.value.trim());

  // Order Number
  const orderNoEl = document.getElementById("order-no");
  result["order-no"] = orderNoEl.value.trim() !== "";

  // Product Code
  const productCodeEl = document.getElementById("product-code");
  result["product-code"] = productCodeEl.value.trim() !== "";

  // Quantity
  const quantityEl = document.getElementById("quantity");
  result["quantity"] = quantityEl.value.trim() !== "" && Number(quantityEl.value) > 0;

  // Complaints group
  const complaintsGroup = document.getElementById("complaints-group");
  const complaints = complaintsGroup.querySelectorAll("input[name='complaints']");
  result["complaints-group"] = Array.from(complaints).some(c => c.checked);

  // Complaint description
  const otherComplaintEl = document.getElementById("other-complaint");
  const complaintDescEl = document.getElementById("complaint-description");
  result["complaint-description"] = !otherComplaintEl.checked || complaintDescEl.value.trim().length >= 20;

  // Solutions group
  const solutionsGroup = document.getElementById("solutions-group");
  const solutions = solutionsGroup.querySelectorAll("input[name='solutions']");
  result["solutions-group"] = Array.from(solutions).some(r => r.checked);

  // Solution description
  const otherSolutionEl = document.getElementById("other-solution");
  const solutionDescEl = document.getElementById("solution-description");
  result["solution-description"] = !otherSolutionEl.checked || solutionDescEl.value.trim().length >= 20;

  return result;
}

// Check if form is valid
function isValid(validationObj) {
  return Object.values(validationObj).every(v => v === true);
}

// Add per-field event listeners
function addListeners() {
  // Full Name
  const fullNameEl = document.getElementById("full-name");
  fullNameEl.addEventListener("change", () => setBorderColor(fullNameEl, fullNameEl.value.trim() !== ""));

  // Email
  const emailEl = document.getElementById("email");
  emailEl.addEventListener("change", () => {
    const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    setBorderColor(emailEl, pattern.test(emailEl.value.trim()));
  });

  // Order Number
  const orderNoEl = document.getElementById("order-no");
  orderNoEl.addEventListener("change", () => setBorderColor(orderNoEl, orderNoEl.value.trim() !== ""));

  // Product Code
  const productCodeEl = document.getElementById("product-code");
  productCodeEl.addEventListener("change", () => setBorderColor(productCodeEl, productCodeEl.value.trim() !== ""));

  // Quantity
  const quantityEl = document.getElementById("quantity");
  quantityEl.addEventListener("change", () => {
    setBorderColor(quantityEl, quantityEl.value.trim() !== "" && Number(quantityEl.value) > 0);
  });

  // Complaints checkboxes
  const complaintsGroup = document.getElementById("complaints-group");
  const complaints = complaintsGroup.querySelectorAll("input[name='complaints']");
  complaints.forEach(c => {
    c.addEventListener("change", () => {
      setBorderColor(complaintsGroup, Array.from(complaints).some(cb => cb.checked));
    });
  });

  // Other complaint description
  const otherComplaintEl = document.getElementById("other-complaint");
  const complaintDescEl = document.getElementById("complaint-description");
  otherComplaintEl.addEventListener("change", () => {
    if (otherComplaintEl.checked) {
      setBorderColor(complaintDescEl, complaintDescEl.value.trim().length >= 20);
    } else {
      complaintDescEl.style.borderColor = "";
    }
  });
  complaintDescEl.addEventListener("input", () => {
    if (otherComplaintEl.checked) {
      setBorderColor(complaintDescEl, complaintDescEl.value.trim().length >= 20);
    }
  });

  // Solutions radio buttons
  const solutionsGroup = document.getElementById("solutions-group");
  const solutions = solutionsGroup.querySelectorAll("input[name='solutions']");
  solutions.forEach(r => {
    r.addEventListener("change", () => {
      setBorderColor(solutionsGroup, Array.from(solutions).some(rb => rb.checked));
    });
  });

  // Other solution description
  const otherSolutionEl = document.getElementById("other-solution");
  const solutionDescEl = document.getElementById("solution-description");
  otherSolutionEl.addEventListener("change", () => {
    if (otherSolutionEl.checked) {
      setBorderColor(solutionDescEl, solutionDescEl.value.trim().length >= 20);
    } else {
      solutionDescEl.style.borderColor = "";
    }
  });
  solutionDescEl.addEventListener("input", () => {
    if (otherSolutionEl.checked) {
      setBorderColor(solutionDescEl, solutionDescEl.value.trim().length >= 20);
    }
  });
}

// Initialize form
document.addEventListener("DOMContentLoaded", () => {
  addListeners();

  const form = document.getElementById("customer-form");
  const statusDiv = document.getElementById("form-status");

  form.addEventListener("submit", e => {
    e.preventDefault();
    const validation = validateForm();
    const formElements = [
      "full-name","email","order-no","product-code","quantity",
      "complaints-group","complaint-description",
      "solutions-group","solution-description"
    ];

    // Update all border colors
    formElements.forEach(id => {
      const el = document.getElementById(id);
      if (!el) return;
      setBorderColor(el, validation[id]);
    });

    // Call isValid with the validation object
    if (isValid(validation)) {
      statusDiv.textContent = "Form submitted successfully!";
      form.reset();
      formElements.forEach(id => {
        const el = document.getElementById(id);
        if (el) el.style.borderColor = "";
      });
    } else {
      statusDiv.textContent = "";
    }
  });
});

.

Hi @BlindVisionMan

Uncaught TypeError: Cannot read properties of null (reading ‘addEventListener’)

You need to resolve the syntax error, which is appearing in the console.

I also noticed your code declares the fullNameEl variable twice.

Happy coding

hi, need clarification for the fields and ids for the javascript. do they need to be like #customer-name, #email, #order-no, #product-code, etc. for the vallidation for the form. also does fcc have strict things for vallidating the valid form and is valid form. need clarification. and what does fcc accept which ids which will pass the challenge and its testing framework? so then i dont have to keep guessing what the field id names are. or can i go with my own id names as long as they vallidate in the form.

thanks

also does the checkboxes and radio group ids need to be the same ids as in the form, or can i use different field names. and then also the check for the checkbox and radio group. what steps does fcc expect for that to pass?.

marvin.

hi, waiting 2 days for some advice please. can some one when they have time and online able to help me out.

marvin.

the html is given for this project, you should not change it, the ids need to stay what they are