Overview
Email validation is a complicated beast. There’s many rules and ways to ensure that an email is correct. So how can we offer a quick and dirty way to pre-validate some emails client side?
What makes a valid email?
An email address might seem straightforward, but there’s more to it than meets the eye. It’s not just [email protected]. There are rules, and email validation is all about making sure those rules are followed.
For starters, an email needs a local part (the bit before the @) and a domain (the bit after). The local part can have letters, numbers, dots, hyphens, and underscores, but not start or end with a dot. Oh, and two dots in a row? Big no-no.
The domain must have at least one dot and a proper top-level domain (like .com or .org). No, “something@invalid_domain” doesn’t fly. The domain name itself should stick to letters, numbers, and hyphens—but nothing too wild, like emojis or spaces.
Some sneaky edge cases:
- Emails can technically use special characters like !#$%&‘*+/=?^_{|}~, but they’re not super common.
- Addresses like “quoted”@example.com are valid too, though they’ll probably break a few systems.
- Domains with fancy TLDs (.london or .pizza, anyone?) are also fine.
So, what makes a valid email? It’s a mix of structure, syntax, and not over-complicating things.
Using Regex (the good, the bad, and the down right ugly)
Regex is like a Swiss Army knife for email validation. It’s compact, sharp, and can do the job. But, just like a Swiss Army knife, if you’re not careful, it can poke you in the eye.
The good:
Regex makes checking for basic email structure easy. You can match patterns like [email protected]
in just one line of code. It’s efficient and gets the job done for most cases. Here’s a simple one to start with:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
This checks that there’s some text before and after the @, and that a dot separates the domain and TLD. For a number use cases, that’s okay.
The bad:
Regex doesn’t understand the full complexity of email rules. It won’t catch typos in domain names (@gamil.com, anyone?) or tell you if the email provider actually exists. Also, too simple a pattern might miss edge cases like [email protected] (which is perfectly valid).
The ugly:
Overly complex regex patterns are where things go wrong. Here’s an example:
const nightmareRegex = /^(?!.*[.]{2})[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
It’s trying to handle everything, but becomes impossible to maintain or debug. Regex like this will work… until it doesn’t. It also doesn’t cover lots of the edge cases. Regex is great for a quick validation, but don’t overcomplicate it. Use it as your first line of defence, not the be-all and end-all. Keep it clean, simple, and don’t be afraid to combine it with other methods when things get messy. A good starting place is checking there’s at least an @ and one “.” for the domain. More than that and you’ll likely start causing issues.
Setting up a simple validation function
The script below is a good example of how email validation can act as a first filter. It ensures users don’t input obviously invalid email formats, but it doesn’t get too picky about edge cases. For that, you can always email them a validation link.
Catching the basics
The isValidEmail function uses a simple regex to check if the email looks valid:
- Something before and after the @.
- A domain with at least one dot.
- That’s it. It doesn’t dig into whether the domain exists or if the email can receive messages. It just says, “Does this roughly look like an email?”
Flexible customisation
The script adds optional attributes to tighten rules, but only if needed:
- Blocking emails with a + symbol (handy if your app doesn’t support email aliases, or you want to stop lots of Gmail signups).
- Preventing common providers like Gmail or Yahoo (Handy if you want business-only emails).
- Blacklisting specific domains with a custom list. (a good first pass at preventing specific nuisance emails, though you’ll want to validate this on the server)
These checks are easy to toggle, and they don’t interfere with basic validation. If a user’s email doesn’t match extra rules, the script explains why. It doesn’t go too deep into actual validation, but more the business rules around what you want to accept as a roughly valid email.
document.addEventListener("DOMContentLoaded", function () {
const emailInputs = document.querySelectorAll('input[type="email"]');
const errorDiv = document.getElementById("email-validation-result");
emailInputs.forEach(input => {
input.addEventListener("input", () => validateEmailInput(input));
input.addEventListener("blur", () => validateEmailInput(input));
});
function validateEmailInput(input) {
const email = input.value.trim();
const blockPlusEmails = input.hasAttribute("block-plus-email");
const blockNonBusinessEmails = input.hasAttribute("block-non-business");
const blockedProviders = input.getAttribute("block-providers");
const providerList = blockedProviders ? blockedProviders.split(",") : [];
let errorMessage = "";
// Check if the email is valid
if (!isValidEmail(email)) {
errorMessage = "Invalid email format.";
}
// Check for plus emails if block-plus-email is set
if (!errorMessage && blockPlusEmails && /\+/.test(getEmailUser(email))) {
errorMessage = "Emails with '+' are not allowed.";
}
// Check for non-business emails if block-non-business is set
if (!errorMessage && blockNonBusinessEmails && isCommonProviderBlocked(email)) {
errorMessage = "Generic email providers are not allowed.";
}
// Check against blocked providers list
if (!errorMessage && providerList.length > 0 && isBlockedProvider(email, providerList)) {
errorMessage = `This email provider is now allowed.`;
}
// Display the error in the div
errorDiv.textContent = errorMessage;
}
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function getEmailUser(email) {
return email.split("@")[0];
}
function isCommonProviderBlocked(email) {
const commonProviders = [
"gmail.com",
"yahoo.com",
"outlook.com",
"hotmail.com",
"aol.com",
"icloud.com",
"mail.com",
"zoho.com",
"gmx.com",
"protonmail.com"
];
const domain = email.split("@")[1];
return commonProviders.includes(domain);
}
function isBlockedProvider(email, blockedProviders) {
const domain = email.split("@")[1];
return blockedProviders.includes(domain);
}
});
Why not go further?
It doesn’t try to be too clever. For example:
- It doesn’t verify if the domain exists (that’s better left to backend checks).
- It doesn’t validate exotic but valid email formats (like “quoted”@example.com).
- It doesn’t assume the user meant to write .com if they wrote .cm.
This keeps the script nice and maintainable and avoids frustrating users with false negatives. It will essentially keep some junk out of your system without overloading your browser or annoying users. For deeper checks - like verifying if the email is real or exists - you can handle that server-side after the form is submitted.
How do I use it with a form?
The following code shows you how easy the javascript is to use with your registration form. You can pull in any validation you like, and you’ll get an error message that explains what’s wrong so users aren’t left frustrated.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Validation</title>
<script src="validator.js"></script>
</head>
<body>
<form>
<input type="email"
block-plus-email
block-non-business
block-providers="example.com,test.com"
placeholder="Enter your email" />
<div id="email-validation-result"></div>
</form>
</body>
</html>
Want to give the above a try? You can try the form below. Some emails you might like to try out are: - [email protected] which will test the “+” rule check - [email protected] which will test the common email/non-business email - example.com which we included in the blocked providers list - an invalid email such as test@test or test+test.com