Build Plate Expectations
You will build a shop for custom license plates, using form
validation and regular expressions. The first steps add checks to
index.html, and the later steps add JavaScript in
practice.js.
See the finished page to know where you are headed, and the lecture if a step uses something you want to review.
Setup
- Open the starter
files on GitHub:
index.htmlandpractice.js. - Make a new folder in WebStorm. For each file, click Copy raw file, the copy icon above the code, and paste it into a new file with the same name.
- Open
index.htmlin the browser, and reload the page after each step.
practice.js has a banner comment for each section below.
Commented-out page lines in a section are there for you to use:
uncomment them where your code needs them.
Step 1: Live Plate
Typing in Plate or Tagline shows on the plate, in capitals. The box's own value changes too, because that is what the form checks.
function typePlate(event) {
event.target.value = event.target.value.toUpperCase();
showPlate();
}
plate.addEventListener("input", typePlate);
document.getElementById("tagline").addEventListener("input", typePlate);Step 2: Attributes
Replace these boxes in index.html:
<input
id="plate"
name="plate"
placeholder="CS213"
required
maxlength="7"
pattern="[A-Z0-9]{2,7}"
title="2 to 7 letters and numbers"
/>
<input
id="tagline"
name="tagline"
placeholder="ROAD TRIP"
maxlength="20"
pattern="[A-Z ]+"
title="letters and spaces"
/>
<input id="name" name="name" placeholder="Ada Lovelace" required />
<input
id="zip"
name="zip"
placeholder="19104"
required
maxlength="5"
inputmode="numeric"
pattern="[0-9]{5}"
title="5 digits"
/>
<input
id="email"
name="email"
placeholder="ada@school.edu"
type="email"
required
pattern="[^ @]+@[^ @]+\.[^ @]+"
title="an address like ada@school.edu"
/>Each box stays in its own spot in the form. Click Order with every box empty: the first box should get a message.
Step 3: Red and Green
Add this under the Red and Green comment in index.html's
<style>. :user-invalid waits until the
box has been used, while :invalid would color empty boxes
as soon as the page loads.
input:user-invalid {
border-color: tomato;
}
input:user-valid {
border-color: mediumseagreen;
}Step 4: Checklist
In showChecklist(), tick the plate rules:
const text = plate.value;
const goodLength = text.length >= 2 && text.length <= 7;
const goodChars = /^[A-Z0-9]+$/.test(text);
document.getElementById("rule-length").classList.toggle("met", goodLength);
document.getElementById("rule-chars").classList.toggle("met", goodChars);Then tick the ZIP and email rules:
const email = document.getElementById("email").value;
const goodZip = document.getElementById("zip").validity.valid;
const goodEmail = /^[^ @]+@[^ @]+\.[^ @]+$/.test(email);
document.getElementById("rule-zip").classList.toggle("met", goodZip);
document.getElementById("rule-email").classList.toggle("met", goodEmail);Under showChecklist();, call it on each
input:
plate.addEventListener("input", showChecklist);
document.getElementById("zip").addEventListener("input", showChecklist);
document.getElementById("email").addEventListener("input", showChecklist);Step 5: Blank Name
A name of only spaces passes required, so check it in
JavaScript. The else clears the message, or the box would
fail forever.
document.getElementById("name").addEventListener("input", (event) => {
if (event.target.value.trim() === "") {
event.target.setCustomValidity("Enter your name");
} else {
event.target.setCustomValidity("");
}
});Step 6: Confirm Email
Email again fails unless it matches Email.
function checkEmails() {
const email = document.getElementById("email").value;
const again = document.getElementById("email-again");
if (again.value !== email) {
again.setCustomValidity("Emails don't match");
} else {
again.setCustomValidity("");
}
}
document.getElementById("email").addEventListener("input", checkEmails);
document.getElementById("email-again").addEventListener("input", checkEmails);Step 7: Error Messages
The browser shows one message at a time. Show each box's message under it when the box loses focus.
for (const box of document.querySelectorAll("#order-form input")) {
box.addEventListener("blur", (event) => {
const error = document.getElementById(`${event.target.id}-error`);
error.textContent = event.target.validationMessage;
});
}Step 8: Place Order
Order is the form's submit button. Stop the reload, stamp the plate, and show a message. The listener runs only when every check passes.
document.getElementById("order-form").addEventListener("submit", (event) => {
event.preventDefault();
const zip = document.getElementById("zip").value;
const text = `Ordered ${plate.value}, shipping to ${zip}`;
document.getElementById("plate-preview").classList.add("ordered");
document.getElementById("message").textContent = text;
});Step 9: Digits Only
A plate needs at least one letter.
function checkDigits() {
if (/^[0-9]+$/.test(plate.value)) {
plate.setCustomValidity("Add at least one letter");
} else {
plate.setCustomValidity("");
}
}
plate.addEventListener("input", checkDigits);Compare
Your page should now match the finished page. If something differs, compare your files with the final code on GitHub.
Extras
Pick any of these once the page works.
Plate Count
<label for="count">Plates</label>
<input id="count" name="count" type="number" min="1" max="5" value="1" required />
<span id="total"></span>function showTotal() {
const count = Number(document.getElementById("count").value);
document.getElementById("total").textContent = `$${count * 35}`;
}
showTotal();
document.getElementById("count").addEventListener("input", showTotal);A count is an amount, so it's a number box.
min and max stop an order of 0 or 6.
Taken Plates
const taken = ["CS213", "HELLO", "GOFAST"];
function checkDigits() {
if (/^[0-9]+$/.test(plate.value)) {
plate.setCustomValidity("Add at least one letter");
} else if (taken.includes(plate.value)) {
plate.setCustomValidity("That plate is taken");
} else {
plate.setCustomValidity("");
}
}Add the else if to checkDigits. A second
listener that set Plate's message would clear this
one's with its else, and the other way around.
Phone
<label for="phone">Phone</label>
<input id="phone" name="phone" type="tel" placeholder="215-555-0123"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" title="like 215-555-0123" />type="tel" shows a phone's number keys and checks
nothing itself, so pattern does the checking. Outside
brackets, - matches itself.