What You'll Learn at This Station
HAP's Discovery: A single HTML page can feel like multiple pages if I control which containers are visible. I learned to keep all my views in the DOM and toggle a .hidden class to show or hide them. Then I ran into the browser's default form behavior and had to learn preventDefault() to stop the page from reloading every time a form was submitted.
View Switching
.hidden class
Two containers in the HTML, one visible at a time. Toggle the .hidden class to move between them. This is how single-page apps work at their core.
preventDefault
event.preventDefault()
Stops the browser's built-in response to an event — like reloading the page when a form submits. Call it first, before reading or processing anything.
Reading Form Data
new FormData(form)
After stopping the reload, read the form's values with FormData. Each input's name attribute becomes a key.
HAP's Confession:
- I started creating a second HTML file for the detail view. Prof. Teeters stopped me. All the content lives on one page — I just control what is visible.
- I used
element.style.display = 'none'inline instead of toggling a CSS class. It worked, but undoing it meant memorizing what display value to restore. A.hiddenclass handles both directions cleanly. - I forgot to hide the summary view before showing the detail view. Both appeared at once. The order matters: add
.hiddento the outgoing view, then remove it from the incoming one. - I submitted my first form, the page reloaded, and all my DOM changes vanished. Spent fifteen minutes wondering why my JavaScript "stopped working." The page had simply restarted.
- I put the submit listener on the submit button instead of the form element. Keyboard users who pressed Enter to submit bypassed my handler entirely.
The View-Switching Pattern
The pattern has three ingredients: two container elements in the HTML, a .hidden CSS rule that sets display: none, and click handlers that add and remove that class. Prof. Teeters' framing helped me see it clearly.
Prof. Teeters:
"You're not navigating to a new page. You're rearranging the room."
The room analogy stuck. Everything is already in the room — some furniture is behind a curtain. Showing the detail view means pulling back the curtain on one area and drawing it across another.
<!-- Two containers — only one visible at a time -->
<div id="summary-view">
<div class="robot-card" data-robot-id="r1">
<h2>Unit R-7</h2>
<p>Status: Active</p>
</div>
</div>
<div id="detail-view" class="hidden">
<button id="back-btn">Back to list</button>
<h2 id="detail-name"></h2>
<p id="detail-status"></p>
</div> The detail view starts with the hidden class applied. The summary view is visible by default. Both containers exist in the DOM from page load.
/* Add this to your stylesheet — one rule does all the hiding */
.hidden {
display: none;
} One rule in the stylesheet does all the work. Toggling a class is cleaner than setting inline styles because it separates concerns: JavaScript manages state, CSS manages appearance.
const summaryView = document.querySelector('#summary-view');
const detailView = document.querySelector('#detail-view');
const backBtn = document.querySelector('#back-btn');
const detailName = document.querySelector('#detail-name');
// Show detail, hide summary
document.querySelector('.robot-card').addEventListener('click', function(event) {
const card = event.currentTarget;
// Populate detail view from the card's content
detailName.textContent = card.querySelector('h2').textContent;
// Swap visibility
summaryView.classList.add('hidden');
detailView.classList.remove('hidden');
});
// Show summary, hide detail
backBtn.addEventListener('click', function() {
detailView.classList.add('hidden');
summaryView.classList.remove('hidden');
}); Add .hidden to the outgoing view and remove it from the incoming one. The back button does the reverse. This is the complete pattern — all SPA view switching is a variation of this.
🟠 The Breakthrough:
When I clicked a robot card and the detail view slid into place, I stopped and looked at the URL bar. It had not changed. It was still one page. "It's one page, but it feels like two." That's exactly what Prof. Teeters meant by rearranging the room.
Forms and preventDefault
The robot detail view had a small form for saving notes. I added a submit listener, typed a note, pressed Enter, and watched the page reload and lose everything. The event had fired — the browser had also done what browsers have done with forms since before JavaScript existed: sent a request and reloaded.
The Browser's Default
Form submission was designed to send data to a server and reload the page with the response. This behavior predates JavaScript's role in form handling and runs automatically unless stopped.
preventDefault()
A method on the event object. Calling it tells the browser: "I will handle this. Do not do the default behavior." For forms, that means no reload. For links, that means no navigation.
Listen on the Form
The submit event fires on the <form> element, not the submit button. Listening on the button only catches mouse clicks — pressing Enter in a text field also submits the form and would bypass a button listener.
Reading Form Values
new FormData(formElement) collects all named inputs. Use formData.get('fieldName') to read a specific input's value. The input must have a name attribute.
Grace Hopper:
"The browser's default behavior predates JavaScript's role in form handling. preventDefault() is not a workaround — it is the intended mechanism for JavaScript-controlled forms."
// Without preventDefault — the page reloads on submit
const notesForm = document.querySelector('#notes-form');
notesForm.addEventListener('submit', function(event) {
// Page reloads here. All DOM changes are lost.
console.log('This never prints because the page reloaded first');
}); The page reloads before any code after the listener call can run. All DOM state is lost. This is exactly what happened to me the first time.
// With preventDefault — the page stays, we handle the data
const notesForm = document.querySelector('#notes-form');
notesForm.addEventListener('submit', function(event) {
event.preventDefault(); // Stop the browser's default reload
// Now we can read the form data
const formData = new FormData(notesForm);
const note = formData.get('note-text');
console.log('Note saved:', note);
// Update the DOM, show a confirmation, do anything we want
}); Call event.preventDefault() as the first line in the submit handler. Then read the data, update the DOM, show confirmations — whatever the application needs.
// MISTAKE: Listening on the submit button instead of the form
const submitBtn = document.querySelector('#submit-btn');
submitBtn.addEventListener('click', function(event) {
event.preventDefault(); // This prevents the click, not the submit
// The form may still submit via keyboard (Enter key)
});
// CORRECT: Listen on the form element for the 'submit' event
const notesForm = document.querySelector('#notes-form');
notesForm.addEventListener('submit', function(event) {
event.preventDefault(); // Catches all submission paths: click, Enter, etc.
}); The 'submit' event fires on the <form> element and covers every submission path. Listening on the button only catches clicks.
View Switching Quick Reference
One Page, Many Views
Put all view containers in the HTML. Start with one visible and the rest .hidden. Toggle the class in response to events. The URL does not change — the DOM does.
The .hidden Class Recipe
Add .hidden { display: none; } to the stylesheet. To show a view: view.classList.remove('hidden'). To hide it: view.classList.add('hidden'). Always hide the outgoing view before revealing the incoming one.
preventDefault on Forms
Always call event.preventDefault() as the first line of a submit handler. Always listen on the <form> element, not the submit button. The submit event fires for all submission paths.
Reading Form Data
const data = new FormData(formElement) then data.get('inputName'). Each input needs a name attribute for FormData to pick it up.
I can switch views and handle forms. But my Robot ID Card page has dozens of cards now. Do I really need to attach a separate click listener to every single one? There has to be a smarter way to handle a whole group of elements at once. 🟠