We are finally ready talk about the key thing that makes Single Page Apps what they are — navigating to between pages without loading new pages.
Right now, you fetch a new page when you click on a link. The most obvious example happens when you click into a Hero, click the back button, then click into the Hero again.
To prevent browsers from fetching new pages, we need to use history to change the URL.
Where to use History?
In Single Page Apps, we want to navigate between pages without loading new pages. This means the link between pages must all use history. In other words, we will be targeting all <a> elements.
We’ll use a global event listener to target all <a> elements.
This works. You can see the link changing, but content on the page doesn’t change.
Why? This happens because we did not re-render the component. So, for history to work properly, we need to re-render the component by putting the event listener inside Tiny.
// Tiny.js
export default function Tiny (comp) {
// ...
if (comp.selector) {
// ...
window.addEventListener('click', event => {
const link = event.target.closest('a')
if (!link) return
if (link.origin !== location.origin) return
event.preventDefault()
history.pushState('', '', link)
_render(comp)
})
}
}
Fixing back and forward buttons
Nothing happens if you click the back button now. That’s because we’re still on the same page, but we haven’t told Tiny to re-render the page again.
Since the back and forward buttons emit a popstate event, we can use this event to re-render the page.
// Tiny.js
export default function Tiny (comp) {
// ...
if (comp.selector) {
// ...
window.addEventListener('click', event => {
const link = event.target.closest('a')
if (!link) return
if (link.origin !== location.origin) return
event.preventDefault()
history.pushState('', '', link)
_render(comp)
})
// Support back and forward buttons
window.addEventListener('popstate', event => {
_render(comp)
})
}
}
Changing the document title
There’s only one thing left to do — we need to tell users what page they’re on by changing the document.title. Once we do this, users will know what this page is about when they’re in a separate tab.
There are two ways of doing this:
We can put the title information in the <a> element as a custom attribute. If we do this, we can use set document.title as we pushState.
We can update the title when the component renders. This means we change the document.title in the template function.
Again, either method works. We’ll go with the second one.
If users land on the Heroes List, we want to let them know they’re on the Heroes List page.
If users land on the hero page, we want to let them know the hero’s name. To do this, we need to use the hero variable that we previously shifted into heroHTML. (So we’ll have to shift it back into template)