JavascriptIntermediate
Password Validator
Build a password validator that checks password strength and shows which rules pass or fail in real time.
Password Validator
Problem
Build a password validator where the user types a password and the app shows in real time which rules pass and which fail. A strength indicator updates as more rules are met.
Input: "abc"
Rules:
❌ At least 8 characters
❌ At least one uppercase letter
✅ At least one lowercase letter
❌ At least one number
❌ At least one special character (!@#$%^&*)
Strength: Weak
Input: "Password1"
Rules:
✅ At least 8 characters
✅ At least one uppercase letter
✅ At least one lowercase letter
✅ At least one number
❌ At least one special character
Strength: Good
Input: "Password1!"
Rules:
✅ At least 8 characters
✅ At least one uppercase letter
✅ At least one lowercase letter
✅ At least one number
✅ At least one special character
Strength: StrongLogic
- Select the password input and all rule elements
- Listen for the
inputevent on the password field - For each rule — test the password against a regex pattern
- Mark each rule as passed or failed
- Count how many rules pass
- Update the strength indicator based on the count
- Update the strength bar width and color
Flow
HTML Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Password Validator</title>
<style>
body {
font-family: sans-serif;
max-width: 440px;
margin: 60px auto;
padding: 0 20px;
}
/* password input wrapper — for show/hide toggle */
.input-wrapper {
position: relative;
margin-bottom: 20px;
}
input[type="password"],
input[type="text"] {
width: 100%;
padding: 12px 44px 12px 12px;
font-size: 1rem;
border: 2px solid #ccc;
border-radius: 8px;
box-sizing: border-box;
outline: none;
}
input:focus {
border-color: #555;
}
/* show/hide password button */
#toggle-btn {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
font-size: 0.85rem;
color: #555;
}
/* strength bar container */
.strength-bar-container {
height: 6px;
background: #e0e0e0;
border-radius: 4px;
margin-bottom: 8px;
overflow: hidden;
}
/* the fill part of the bar */
#strength-bar {
height: 100%;
width: 0%;
border-radius: 4px;
transition: width 0.3s ease, background 0.3s ease;
}
/* strength label */
#strength-label {
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 16px;
color: #888;
}
/* rules list */
.rules {
list-style: none;
padding: 0;
margin: 0;
}
.rules li {
display: flex;
align-items: center;
gap: 10px;
padding: 7px 0;
font-size: 0.9rem;
color: #888;
border-bottom: 1px solid #f0f0f0;
transition: color 0.2s;
}
.rules li:last-child {
border-bottom: none;
}
/* icon shown before each rule */
.rules li .icon {
font-size: 1rem;
width: 20px;
text-align: center;
}
/* passed rule — green */
.rules li.passed {
color: #1a7a3f;
}
/* failed rule — default grey */
.rules li.failed {
color: #aaa;
}
</style>
</head>
<body>
<h1>Password Validator</h1>
<!-- password input with show/hide toggle -->
<div class="input-wrapper">
<input type="password" id="password-input" placeholder="Enter password..." />
<button id="toggle-btn">Show</button>
</div>
<!-- strength bar -->
<div class="strength-bar-container">
<div id="strength-bar"></div>
</div>
<!-- strength label -->
<div id="strength-label">Enter a password</div>
<!-- rules list — each li has a data-rule attribute matching the rules object -->
<ul class="rules">
<li data-rule="minLength">
<span class="icon">○</span>
At least 8 characters
</li>
<li data-rule="uppercase">
<span class="icon">○</span>
At least one uppercase letter
</li>
<li data-rule="lowercase">
<span class="icon">○</span>
At least one lowercase letter
</li>
<li data-rule="number">
<span class="icon">○</span>
At least one number
</li>
<li data-rule="special">
<span class="icon">○</span>
At least one special character (!@#$%^&*)
</li>
</ul>
<script src="script.js"></script>
</body>
</html>Solution
// Step 1 — select elements
const passwordInput = document.querySelector("#password-input");
const toggleBtn = document.querySelector("#toggle-btn");
const strengthBar = document.querySelector("#strength-bar");
const strengthLabel = document.querySelector("#strength-label");
// Step 2 — define the rules
// each rule has a name, a regex to test against, and a label
const rules = {
minLength: {
regex: /.{8,}/, // 8 or more characters
label: "At least 8 characters"
},
uppercase: {
regex: /[A-Z]/, // at least one uppercase letter
label: "At least one uppercase letter"
},
lowercase: {
regex: /[a-z]/, // at least one lowercase letter
label: "At least one lowercase letter"
},
number: {
regex: /[0-9]/, // at least one digit
label: "At least one number"
},
special: {
regex: /[!@#$%^&*]/, // at least one special character
label: "At least one special character"
}
};
// Step 3 — define strength levels
// each level has a label, color for the bar, and width percentage
const strengthLevels = [
{ label: "", color: "#e0e0e0", width: "0%" }, // 0 rules
{ label: "Weak", color: "#e74c3c", width: "20%" }, // 1 rule
{ label: "Weak", color: "#e74c3c", width: "40%" }, // 2 rules
{ label: "Fair", color: "#f39c12", width: "60%" }, // 3 rules
{ label: "Good", color: "#3498db", width: "80%" }, // 4 rules
{ label: "Strong", color: "#1a7a3f", width: "100%" } // 5 rules
];
// Step 4 — listen for input event
passwordInput.addEventListener("input", validatePassword);
function validatePassword() {
const password = passwordInput.value;
// Step 5 — test each rule and count how many pass
let passedCount = 0;
for (const [ruleName, rule] of Object.entries(rules)) {
// test the password against this rule's regex
const passed = rule.regex.test(password);
if (passed) passedCount++;
// Step 6 — find the matching li element using data-rule attribute
const ruleElement = document.querySelector(`[data-rule="${ruleName}"]`);
const icon = ruleElement.querySelector(".icon");
// update the class and icon based on pass/fail
if (passed) {
ruleElement.classList.add("passed");
ruleElement.classList.remove("failed");
icon.textContent = "✅";
} else {
ruleElement.classList.add("failed");
ruleElement.classList.remove("passed");
icon.textContent = "○";
}
}
// Step 7 — update the strength bar and label
const level = strengthLevels[passedCount];
strengthBar.style.width = level.width;
strengthBar.style.background = level.color;
strengthLabel.textContent = password.length === 0
? "Enter a password"
: level.label || "Weak";
strengthLabel.style.color = level.color;
}
// Step 8 — show/hide password toggle
toggleBtn.addEventListener("click", () => {
const isPassword = passwordInput.type === "password";
passwordInput.type = isPassword ? "text" : "password";
toggleBtn.textContent = isPassword ? "Hide" : "Show";
});Code Execution
Trace through typing "Password1!":
| Rule | Regex | Test | Result |
|---|---|---|---|
minLength | /.{8,}/ | "Password1!".length >= 8 | ✅ pass |
uppercase | /[A-Z]/ | contains P | ✅ pass |
lowercase | /[a-z]/ | contains assword | ✅ pass |
number | /[0-9]/ | contains 1 | ✅ pass |
special | /[!@#$%^&*]/ | contains ! | ✅ pass |
| Passed count | 5 | Strength: Strong | |
| Bar | width 100%, color #1a7a3f | green full bar |
Trace through typing "abc":
| Rule | Regex | Test | Result |
|---|---|---|---|
minLength | /.{8,}/ | "abc".length < 8 | ❌ fail |
uppercase | /[A-Z]/ | no uppercase | ❌ fail |
lowercase | /[a-z]/ | contains abc | ✅ pass |
number | /[0-9]/ | no number | ❌ fail |
special | /[!@#$%^&*]/ | no special | ❌ fail |
| Passed count | 1 | Strength: Weak | |
| Bar | width 20%, color #e74c3c | red short bar |
Output
Type "abc" → 1/5 rules pass — Weak (red bar 20%)
Type "Password1" → 4/5 rules pass — Good (blue bar 80%)
Type "Password1!" → 5/5 rules pass — Strong (green bar 100%)
Empty input → "Enter a password"