Changing Dom Content
The router knows the URL. Now something has to update the DOM. A switch statement maps routes to view elements, and a single container element holds the current page.
A URL change triggers nothing by itself. pushState updates the address bar and nothing else. I learned this the hard way: the DOM stays on the previous page until something explicitly swaps it. That something is renderRoute -- a function that maps a path to a view element and puts it on screen.
The Essentials
renderRouteas the view layer: A function that takes a route string and returns the correct page element. The router calls it; it handles the DOM.- Switch statement for routes: The most readable way to map a small set of URL paths to corresponding view elements.
- Single container element: One
<main>element holds the current page. Navigation clears it and inserts the new page element. childrenvschildNodes:childrenreturns only element nodes.childNodesreturns all nodes including text nodes and comments. For removing rendered page content,childrenis the right choice.- Resetting scroll position: After a route change,
scrollXandscrollYon the window should be reset to zero so the new page starts at the top.
The renderRoute Function
// router.js
import MenuPage from './pages/MenuPage.js';
import DetailsPage from './pages/DetailsPage.js';
import CartPage from './pages/CartPage.js';
export function renderRoute(route) {
let pageElement;
switch (route) {
case '/':
pageElement = MenuPage.create();
break;
case '/cart':
pageElement = CartPage.create();
break;
default:
pageElement = MenuPage.create();
}
const main = document.querySelector('main');
if (main.children[0]) {
main.children[0].remove();
}
main.appendChild(pageElement);
window.scrollX = 0;
window.scrollY = 0;
}The switch maps each known route to a page factory. The default case handles unknown routes by falling back to the menu - the same role a 404 page would serve in a server-rendered app.
Page Elements as Factory Functions
Each page is a module that exports a create function. The function builds and returns a DOM element. It does not know about the router or the container - it only knows how to build its own markup.
// pages/MenuPage.js
const MenuPage = {
create() {
const section = document.createElement('section');
section.id = 'menu';
// ... populate with items from Store
return section;
}
};
export default MenuPage;renderRoute calls create(), gets back an element, and inserts it. The page itself stays decoupled from navigation.
Clearing the Container: children vs childNodes
Before inserting the new page, the old one has to go. There are two ways to read what is in the container.
main.children returns an HTMLCollection of child elements only. Text nodes, comments, and whitespace in the HTML are excluded.
main.childNodes returns a NodeList of all child nodes: elements, text nodes, comment nodes, and anything else.
For clearing a container that holds rendered page elements, children is the right tool:
if (main.children[0]) {
main.children[0].remove();
}Using childNodes[0] could accidentally grab a text node (a newline, for instance, left by whitespace in the HTML). That node removal would succeed without clearing the visible page.
Why Not innerHTML = ''
Setting innerHTML = '' is a common shortcut for clearing a container. It works, but it bypasses event listener cleanup. If the page element has child elements with registered handlers, those handlers are not removed - they simply become unreachable as the element is discarded. For short-lived small apps this is fine. For a long-running SPA with heavy views, it can become a source of memory growth over time.
The remove() approach is more deliberate: remove the specific element you know is there.
Scroll Reset
After a route change, the window scroll position carries over from the previous page. A user who scrolled to the bottom of the menu page should not see the cart page loaded mid-scroll.
window.scrollX = 0;
window.scrollY = 0;These assignments reset the scroll to the top-left corner after each route change. In most SPAs the only relevant axis is scrollY (vertical), but resetting both costs nothing.
Further Reading and Watching
- MDN: Element.children - Returns the live HTMLCollection of child elements, excluding text nodes.
- MDN: Node.childNodes - Returns a NodeList of all child nodes including text and comment nodes.
Video:
- JavaScript DOM Crash Course - Part 2 by Traversy Media. Covers DOM manipulation including createElement, appendChild, and removing elements.
Keep reading