HAP surrounded by swirling page panels, learning to swap views

Station 3: Swapping Views

View switching, forms, and preventDefault

Welcome to Station 3! My robot cards were working — I could click them and handle the event. But I wanted clicking a card to reveal a full detail view, with a back button that returned to the list. The only way I knew to show a different page was to make a second HTML file.

Prof. Teeters stopped me before I opened a new tab. "You're not navigating to a new page," he said. "You're rearranging the room."

That one sentence changed how I think about single-page interactions. The content is already there — I just need to control which parts are visible. And when I added a form to the detail view, I discovered a second trap: the browser's default behavior.

Let me show you how I learned to swap views and tame form submissions... 🟠

🔬 Try it yourself: See View Switching in Action →

Quick Reference

The View-Switching Pattern →

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 surrounded by tangled code with an oops expression

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 .hidden class handles both directions cleanly.
  • I forgot to hide the summary view before showing the detail view. Both appeared at once. The order matters: add .hidden to 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-view HTML structure:
<!-- 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.

The .hidden CSS rule (add to your stylesheet):
/* 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.

Swapping between views:
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 reload trap:
// 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 correct pattern:
// 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.

The button vs. form listener mistake:
// 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

1

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.

2

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.

3

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.

4

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. 🟠

Quick Reference

The View-Switching Pattern →