Interactive Registration Form with a Socratic Tutor
In this lab, you will build an interactive user registration form with real-time validation and localStorage persistence, and then use Claude as a strict Socratic tutor to deepen your understanding of how the events in your own code travel through the browser.
Estimated Time: 2.5 hours, asynchronous (approximately 1 hour 45 minutes for Part A and 30 to 45 minutes for Part B).
Workplace Context
Imagine you are a junior front-end developer at a startup. Your team is building a new web application, and your first task is to create the client-side functionality for the user registration page. It is crucial that this form is user-friendly, provides clear validation feedback to prevent errors, and remembers some basic user input for convenience. After you ship a feature, your tech lead also expects you to explain how it works in a code review: not just what the code does, but how the browser processes each event behind the scenes. This lab simulates both parts of that job. You will build the form, and then you will prove your understanding of its event handling by teaching it back during a question-driven session with an AI tutor.
Objectives
By the end of this lab, you will be able to:
- Structure an HTML form with appropriate input fields for registration.
- Implement real-time input validation using JavaScript event listeners (
inputevent). - Use HTML5 validation attributes (e.g.,
required,type,minlength,pattern). - Apply the JavaScript Constraint Validation API to check validity and display custom error messages.
- Dynamically create and display error messages next to input fields.
- Handle the form
submitevent, prevent default submission, and perform final validation. - Use
localStorageto save and retrieve simple form data (e.g., username). - Formulate prompts that direct an AI assistant to act strictly as a Socratic tutor.
- Explain, in your own words, the capture, target, and bubble phases that each event in your code travels through, from the browser
windowdown to the target element and back.
Part A: Build the Registration Form
Part A is the same build as the standard version of this lab. Complete it entirely yourself, without AI assistance. Part B depends on the code being your own: you cannot explain code you did not write, and your Socratic tutor will only ask questions about the listeners you implemented.
Step 1: Set Up the Project
- Create a new folder named
interactive-registration-form. - Inside this folder, create the following files:
index.htmlstyles.cssscript.js
- Initialize the folder as a Git repository so you can commit your progress and push it to GitHub for submission:
cd interactive-registration-form
git initStep 2: Build the HTML Structure
Create the basic HTML structure for your registration form in index.html. Include fields for: Username, Email, Password, and Confirm Password. Also, add a submit button. Ensure each input has a corresponding <span> element for error messages.
We have included an example below, for convenience, but you will need to modify it to include validation attributes (such as required, minlength, and pattern) and any other elements you need.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">
<title>Interactive Registration Form</title>
</head>
<body>
<div class="container">
<h1>Register</h1>
<form id="registrationForm" novalidate>
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" name="username">
<span class="error-message" id="usernameError"></span>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<span class="error-message" id="emailError"></span>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<small>Password must be at least 8 characters long, include an uppercase letter, a lowercase letter, and a number.</small>
<span class="error-message" id="passwordError"></span>
</div>
<div class="form-group">
<label for="confirmPassword">Confirm Password:</label>
<input type="password" id="confirmPassword" name="confirmPassword">
<span class="error-message" id="confirmPasswordError"></span>
</div>
<button type="submit">Register</button>
</form>
</div>
<script src="script.js"></script>
</body>
</html>Step 3: Style the Application
Add some basic styles in styles.css to make the form presentable and to style the error messages. An example is provided below, for convenience, but you can modify it as you see fit.
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}
.container {
background-color: #fff;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
}
h1 {
text-align: center;
color: #333;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input[type="text"],
.form-group input[type="email"],
.form-group input[type="password"] {
width: calc(100% - 22px); /* Account for padding and border */
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box; /* Makes width calculation easier */
}
.form-group input:invalid {
border-color: #e74c3c; /* Light red for invalid built-in states */
}
/* You can add a .valid class if you want to style valid inputs too */
.form-group .error-message {
display: block;
color: #e74c3c; /* Red for error messages */
font-size: 0.9em;
margin-top: 5px;
min-height: 1em; /* Reserve space to prevent layout shifts */
}
.form-group small {
display: block;
font-size: 0.8em;
color: #777;
margin-top: 3px;
}
button[type="submit"] {
width: 100%;
padding: 10px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
button[type="submit"]:hover {
background-color: #2980b9;
}Step 4: Implement the JavaScript
In script.js, implement the core validation logic.
- Select all necessary DOM elements (form, inputs, error message spans).
- Load saved username: On page load, check if a username is saved in
localStorage. If so, pre-fill the username field. - Real-time validation: Add
inputevent listeners to each field.- Check validity using the Constraint Validation API (
inputElement.validity). - For the “Confirm Password” field, explicitly check if it matches the “Password” field.
- Display appropriate custom error messages in the corresponding
<span>elements. Clear messages if valid.
- Check validity using the Constraint Validation API (
- Form submission: Add a
submitevent listener to the form.- Call
event.preventDefault(). - Perform a final validation check on all fields.
- If all fields are valid:
- Display a success message (e.g., an
alertor update a status message on the page). - Save the username to
localStorage. - Optionally, reset the form.
- Display a success message (e.g., an
- If any field is invalid, ensure error messages are displayed and focus on the first invalid field.
- Call
Ensure your JavaScript is well-commented. Commit your work as you complete each piece of functionality.
Step 5: Test and Validate
- Test Basic Registration: Fill out all fields with valid data and submit the form. Verify the success message and that the username is saved in
localStorage(check your browser’s Developer Tools > Application > Local Storage). - Test Username Validation:
- Try submitting with an empty username.
- Enter a username that is too short.
- Verify error messages appear in real time as you type (or on blur/submit).
- Test Email Validation:
- Try submitting with an empty email.
- Enter an invalid email format (e.g., “test@”, “test.com”).
- Test Password Validation:
- Try submitting with an empty password.
- Enter a password that is too short.
- Enter a password that does not meet the pattern (e.g., all lowercase, no numbers).
- Ensure the “Confirm Password” field shows an error if it does not match the password.
- Test Local Storage Persistence: After a successful registration, refresh the page. The username field should be pre-filled with the value you entered.
- Edge Cases: Think about what happens if a user tries to bypass validation (client-side validation is mainly for UX; server-side validation is for security). What happens if
localStorageis full or disabled? For this lab, we assume it works, but it is a real-world consideration.
Checkpoint: Before moving on to Part B, your form must validate in real time as you type, block invalid submissions, show custom error messages, and pre-fill the username from localStorage after a refresh.
Part B: Socratic Tutor Session
You will now use Claude in the AI as Socratic Tutor role. In this role, the AI is explicitly forbidden from writing code or giving direct answers. It may only ask you guiding questions, and you supply every explanation yourself. The goal is for you to be able to trace, out loud and in writing, the exact path each event in your code travels: from the browser window, down through the document to the target element (the capture phase), through the target phase, and back up to the window (the bubble phase). This builds on the propagation phases you studied in Lesson 4. Note that the Lesson 4 diagrams begin the capture phase at document; the full path the browser dispatches actually begins one level higher, at the window object itself, and that is the path you will map in this lab.
Step 1: Set Up the Socratic Tutor Role
Give Claude the following role prompt, exactly as written, before any other message:
You are my Socratic tutor for DOM events. You must not write, complete, or correct any code, and you must not state final answers. Respond only with one guiding question at a time. When my explanation is accurate, confirm it briefly and ask a deeper question. If I ask you for the answer, decline and rephrase your question.Choose one of the following setups:
Option 1: claude.ai (recommended)
- Sign in at https://claude.ai with your Per Scholas-provided Claude Pro account.
- Create a new Project and paste the role prompt above into the Project’s custom instructions. This is the most reliable way to enforce the role, because the instructions apply to every conversation inside that Project. If you prefer not to use a Project, start a new chat and send the role prompt as your very first message instead.
- Paste the relevant portion of your
script.jsinto the chat when a prompt refers to it, so the dialogue is grounded in your actual code.
Option 2: Claude Code (terminal)
- If you have not installed Claude Code yet, run
npm install -g @anthropic-ai/claude-code(Node.js 18 or later is required, which you already have). - Open a terminal in your
interactive-registration-formfolder and runclaude. Log in with your Per Scholas-provided Claude account if prompted (/login). - Paste the role prompt as your first message. Because Claude Code can read your files directly, you can then ground the dialogue in your real code, for example: “Read script.js. Using only questions, tutor me on the submit listener defined there.”
- Claude Code asks permission before editing files or running commands. In this lab it should never need to do either; if it proposes a file edit, decline it. The tutor asks questions, nothing more.
If you do not have access to a Pro-level Claude account through Per Scholas, you may complete this lab using the free tier of Claude or another AI assistant with equivalent capabilities. Free-tier usage limits may require you to complete the lab across more than one session. The Socratic dialogues in this lab are short, so the free tier of claude.ai is normally sufficient; use the same role prompt as your first message in a regular chat.
Step 2: Formulate Your Three Socratic Prompts
This step is the core of the lab, so do it carefully and before you start any dialogue. You will write three prompts, one for each of the following listeners in your own script.js:
- The
inputevent listener on the email field. - The
submitevent listener on the form, including your use ofevent.preventDefault(). - One listener of your choice: a
blurlistener or achangelistener if your implementation includes one, or theinputlistener that performs your confirm-password match check (every implementation from Part A includes this one).
Each prompt must:
- Name the exact event type and element the listener is attached to (for example, “the
inputlistener on the element with the idemail”). - Direct the tutor to lead you, through questions only, to explain:
- What user action dispatches the event.
- The capture path the event travels from
window, throughdocumentand every ancestor element of the target in your HTML, down to the target element. - What happens during the target phase.
- The bubble path the event travels back up to
window. - Which default browser behavior is involved, and what your code does about it (this matters most for the
submitlistener).
Here is a worked example for the email field. Do not copy it word for word; adapt it to your own code, and write your other two prompts yourself:
My script.js attaches an input event listener to the email field, the input element with the id "email", inside the form with the id "registrationForm". Acting only as my Socratic tutor, ask me one question at a time to lead me to explain: what user action dispatches the input event, the capture path the event travels from the window down through the document and my page structure to the email input, what happens during the target phase, the bubble path the event travels back up to the window, and how my validation logic runs as part of that journey. Do not explain anything yourself and do not show any code. Only ask questions.A well-formed Socratic prompt meets all four points of this checklist:
| Check | Question to ask yourself |
|---|---|
| Names the listener | Does the prompt state the exact event type (input, submit, blur, change)? |
| Names the element | Does the prompt identify the specific element by id or role in your HTML? |
| Constrains the AI | Does the prompt restrict the AI to questions only, with no code and no answers? |
| Targets the window | Does the prompt require the full propagation path, from window to the target and back, plus the default browser behavior involved? |
Record all three prompts in a new file named SOCRATIC-DIALOGUE.md in your project folder before you begin.
Checkpoint: All three prompts are written down in SOCRATIC-DIALOGUE.md before you start any dialogue. Grading requires the prompts to appear verbatim, exactly as you sent them.
Step 3: Run the Dialogues
Run one dialogue per prompt, in order.
- Send your prompt to the tutor.
- Answer every question in your own words. Look at your own
script.jsandindex.htmlwhile you answer; the point is to connect the concepts to your actual code. - Do not ask the tutor for answers, hints phrased as answers, or code. If you get stuck, say what you think is true and let the next question steer you.
- If the AI drifts into lecturing, explaining, or writing code, restate the role constraint: “Remember, you are my Socratic tutor. Do not explain or write code. Ask me your question again.” Recovering the role when the AI drifts is part of the skill this lab teaches.
- Continue each dialogue until you have explained, and the tutor has confirmed, the full propagation path and the default behavior for that listener. A focused dialogue usually takes 6 to 12 exchanges.
- Save the evidence: use the Share button in claude.ai to create a public link to the conversation, or copy the full transcript into
SOCRATIC-DIALOGUE.md. If sharing is disabled in your workspace, or you used Claude Code, copy the transcript text instead.
Step 4: Map Each Event’s Path Through the Browser
After each dialogue, and without the AI’s help, produce two artifacts in SOCRATIC-DIALOGUE.md for that listener:
- An event-flow map (plain text or ASCII) showing the capture path from
windowdown to the target, the target phase, and the bubble path back up. Trace your actual HTML structure: every element between<body>and the target appears on the path. Using the starter HTML, a map for the email listener would look like this:
CAPTURE PHASE (window down to target)
window
-> document
-> html
-> body
-> div.container
-> form#registrationForm
-> div.form-group
-> input#email
TARGET PHASE
input#email (my "input" listener runs here)
BUBBLE PHASE (target back up to window)
input#email
-> div.form-group
-> form#registrationForm
-> div.container
-> body
-> html
-> document
-> window- A written explanation of 3 to 5 sentences, in your own words, connecting the listener to browser window behavior. For example: for the
submitlistener, explain why the browser would navigate away from the page ifevent.preventDefault()were omitted, and where in the event’s journey your handler intercepts it; for aninputlistener, explain how event delegation in general relies on the bubble phase you just mapped.
Two accuracy notes for your maps:
- The target of the
submitevent is the form element itself, not the button you clicked. Your second map should therefore end its capture path atform#registrationForm. - The
blurevent captures down to the target but does not bubble. If you chosebluras your third listener, your map should show the capture and target phases only, and your explanation should point out this difference. Let the tutor lead you to discover it.
Checkpoint: SOCRATIC-DIALOGUE.md now contains three prompts, three dialogue transcripts (or shared conversation links), three event-flow maps, and three written explanations.
Reflection Questions
Include your answers to the following questions in your submission, within the README.md file:
- How did
event.preventDefault()help in handling form submission? - What is the difference between using HTML5 validation attributes and JavaScript-based validation? Why might you use both?
- Explain how you used
localStorageto persist and retrieve the username. What are the limitations oflocalStoragefor storing sensitive data? - Describe a challenge you faced in implementing the real-time validation and how you solved it.
- How did you ensure that custom error messages were user-friendly and displayed at the appropriate times?
- Which of your three prompts best constrained the AI to the Socratic method, and why?
- What did the questioning process reveal about event propagation that reading documentation had not?
Submission Guidelines
Submit your project via a GitHub repository link using the Start Assignment link on Canvas, or as instructed by your facilitator. Your repository must contain:
index.html,styles.css, andscript.js(the completed registration form).README.mdwith your answers to all seven reflection questions.SOCRATIC-DIALOGUE.mdcontaining, for each of the three listeners: your prompt exactly as you sent it, the full dialogue transcript or a shared conversation link, your event-flow map, and your 3 to 5 sentence written explanation.
You may structure SOCRATIC-DIALOGUE.md like this:
# Socratic Dialogue: DOM Events
## 1. The input listener on the email field
### My Prompt
### Dialogue (transcript or shared link)
### Event-Flow Map
### My Explanation
## 2. The submit listener on the form
...
## 3. My chosen listener: <event type> on <element>
...This lab is graded complete/incomplete. To receive a complete, your submission must satisfy every item on the facilitator checklist:
- All functional requirements of the registration form are met: real-time validation with
inputevent listeners, HTML5 validation attributes, the Constraint Validation API with custom error messages,submithandling withevent.preventDefault()and final validation, and username persistence withlocalStorage. SOCRATIC-DIALOGUE.mdcontains your three formulated Socratic prompts verbatim.- The transcript or shared conversation link demonstrates a genuine Socratic exchange: the AI asked questions, you supplied the explanations, and the AI wrote no code.
- All three event-flow maps correctly show the capture, target, and bubble phases from
windowto the target element (with theblurexception noted if you chose that event). - Each map is accompanied by a 3 to 5 sentence explanation in your own words connecting the listener to browser window behavior (for example, what happens to page navigation if
event.preventDefault()is omitted).