Skip to Content
Course content

84: Working with Forms and Validation

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Let's build a signup form for a developer portfolio site. I want a simple setup: a username, an email, and a password. In a perfect world, the browser handles everything, but in reality, we need JavaScript to bridge the gap between "the user typed something" and "the data is actually usable."

The "Refresh" Headache

I'll start with some basic HTML and a script to handle the submission. Here is my first attempt:

<form id="signup-form">
  <input type="text" name="username" placeholder="Username">
  <input type="email" name="email" placeholder="Email">
  <input type="password" name="password" placeholder="Password">
  <button type="submit">Join</button>
</form>

<script>
const form = document.querySelector('#signup-form');
form.addEventListener('submit', (e) => {
  console.log('Form submitted!');
});
</script>

If you run this and click "Join," you'll notice something annoying: the console log flashes for a millisecond, and then the page refreshes, clearing all the inputs. This is the default behavior of HTML forms—they try to send a GET or POST request to the current URL. Since we're building a modern app, we want to handle this with JS without the page blinking. I need to stop that default action.

form.addEventListener('submit', (e) => {
  e.preventDefault(); 
  console.log('Now we are in control.');
});

Grabbing Everything at Once

Now I need the data. My first instinct is usually to grab every input by its ID: document.getElementById('username').value, and so on. But if I add ten more fields, my code becomes a wall of repetitive selectors. It's tedious.

Instead, I'll use the FormData object. It's a built-in JS class that sucks up all the named inputs from a form automatically. Let's see what happens when I wrap the form element in it:

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const data = new FormData(e.target);
  console.log(data); // Wait, this just logs a FormData object, not the values.
});

That's a common "gotcha." FormData is an iterator, not a plain object. To actually see the data in a format I can use (like an object), I'll spread it into Object.fromEntries(). This is much cleaner:

const formData = new FormData(e.target);
const entries = Object.fromEntries(formData.entries());
console.log(entries); // { username: 'dev_jane', email: 'jane@example.com', password: '...' }

Beyond the HTML5 Basics

I've used type="email" in the HTML, which handles basic format checking. But what if I want a specific rule? For example, the password must be at least 8 characters. HTML's minlength attribute is okay, but it doesn't give me much control over how the error is displayed.

I'll try to implement a custom check. I'll create a simple validation function and call it during the submit event:

const validate = (data) => {
  if (data.password.length < 8) {
    return 'Password is too short!';
  }
  if (!data.username.includes('_')) {
    return 'Username must contain an underscore.';
  }
  return null; // No errors
};

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const data = Object.fromEntries(new FormData(e.target));
  const error = validate(data);
  
  if (error) {
    alert(error); // This works, but alerts are terrible UX.
  } else {
    console.log('Success!');
  }
});

Making it User-Friendly

Using alert() is lazy and interrupts the user. I want the error to appear right on the page. I'll add a small empty div to my HTML to act as an error container, then I'll manipulate its text and style based on the validation result.

<div id="error-msg" style="color: red;"></div>
<form id="signup-form">...</form>

<script>
const errorDiv = document.querySelector('#error-msg');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const data = Object.fromEntries(new FormData(e.target));
  const error = validate(data);

  if (error) {
    errorDiv.textContent = error;
    form.classList.add('shaking'); // I'd add a CSS animation here for flair
  } else {
    errorDiv.textContent = '';
    console.log('Form submitted successfully');
  }
});
</script>

By following this flow—preventing the refresh, using FormData for efficiency, and moving from alert() to DOM manipulation—we've built a validation pipeline that is flexible and doesn't annoy the user.




📋 Practical Task

The Secure Portfolio Registration Validator

Build a registration form for a portfolio site that includes the following fields: Username, Email, and Password.

  • Prevent the form from refreshing the page on submission.
  • Use FormData and Object.fromEntries to capture the input values.
  • Implement a validation check that ensures:
    • The username is at least 3 characters long.
    • The password is at least 8 characters long.
  • Instead of an alert, display the specific error message in a dedicated <p> element located above the form.
  • If the validation passes, clear the error message and log "Registration Successful" to the console.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.