The Pattern at a Glance
Add a .hidden CSS rule
One rule in the stylesheet: .hidden { display: none; }. Every view container uses this class. Never set display: none inline — toggling a class is reversible without knowing what display value to restore.
Start one view visible, rest hidden
All view containers live in the HTML from page load. Apply the hidden class in the HTML to all views except the default one. JavaScript toggles visibility — it does not create or destroy containers.
Hide outgoing before showing incoming
classList.add('hidden') on the departing view, then classList.remove('hidden') on the arriving view. Reversing the order causes both views to appear simultaneously for a moment.
Listen on the form, not the button
The 'submit' event fires on the <form> element. A listener on the submit button only catches mouse clicks — pressing Enter in a text field submits the form without clicking the button.
View Switching Code
/* One CSS rule handles all view hiding */
.hidden {
display: none;
} <!-- Two view containers. One starts hidden. -->
<div id="summary-view">
<!-- list of items here -->
</div>
<div id="detail-view" class="hidden">
<button id="back-btn">Back to list</button>
<!-- detail content here -->
</div> const summaryView = document.querySelector('#summary-view');
const detailView = document.querySelector('#detail-view');
const backBtn = document.querySelector('#back-btn');
// Go to detail view
function showDetail() {
summaryView.classList.add('hidden'); // hide outgoing first
detailView.classList.remove('hidden'); // then show incoming
}
// Return to summary view
backBtn.addEventListener('click', function() {
detailView.classList.add('hidden');
summaryView.classList.remove('hidden');
}); Forms and preventDefault
// Always listen on the <form> element, not the submit button
const form = document.querySelector('#my-form');
form.addEventListener('submit', function(event) {
event.preventDefault(); // Call first — stops the page reload
// Now read the data
const data = new FormData(form);
const value = data.get('field-name'); // Matches the input's name attribute
// Update the DOM, show a message, etc.
console.log('Form value:', value);
}); // Reading multiple fields from a form
// HTML: <input name="title"> <input name="notes"> <select name="status">
form.addEventListener('submit', function(event) {
event.preventDefault();
const data = new FormData(form);
const title = data.get('title'); // string from text input
const notes = data.get('notes'); // string from textarea
const status = data.get('status'); // selected option value
console.log({ title, notes, status });
}); Each input needs a name attribute for FormData to pick it up. The name attribute is the key used with data.get().
Common Mistakes
// MISTAKE 1: Forgetting to hide the outgoing view
summaryView.classList.remove('hidden'); // Both views show at once!
// CORRECT: hide first, then show
summaryView.classList.add('hidden');
detailView.classList.remove('hidden');
// MISTAKE 2: Listening on the submit button instead of the form
submitBtn.addEventListener('click', function(event) {
event.preventDefault(); // Misses Enter-key submissions
});
// CORRECT: Listen on the form
form.addEventListener('submit', function(event) {
event.preventDefault(); // Catches all submission paths
});
// MISTAKE 3: Using inline style instead of a class
element.style.display = 'none'; // Hard to reverse cleanly
element.style.display = 'block'; // What if it was 'flex' or 'grid'?
// CORRECT: toggle a class
element.classList.add('hidden');
element.classList.remove('hidden');