One of the features of a Progressive Web App is the lack of browser chrome on a mobile device. iOS and Android hide little things like the address bar and the browser history buttons. It’s up to the PWA itself to provide the navigation to previous pages.
Breadcrumbs are one technique of displaying previously visited pages on the web page, and they are usually stored on the server side and rendered into the HTML sent over the wire. Another technique is to store them in the browser tab’s sessionStorage
and render them on the page. This technique fits neatly inside a Stimulus.js controller that listens for page changes from the beforeunload
and the turbolinks:before-visit
events. The history_controller.js
listens for page change events, and records the current page into a history of the session. This history is stored and read from the browser’s sessionStorage
, a key-value store that is kept around only during the lifetime of a tab.
The HTML
Our breadcrumb HTML is really simple. The example uses an unordered list, and when connected, loads the items from history into individual list items. The only HTML needed is:
<ul data-controller="history"
data-target="history.links"
class="history">
</ul>
The ul
hosts the controller, has a target which is used to append the entries, and a css class in order to style the breadcrumbs to fit the pages style.
Some CSS
In order for the list to appear horizontal, this demo uses the following CSS:
ul.history li {
display: inline;
list-style-type: none;
padding-right: 1em;
}
Stimulus Templating
Inside the Stimulus controller, there is a function that will generate the individual list items, and the HTML is:
<li>
<a href="${ historyItemPath }"
data-action="history#visitPageInHistory"
data-history-location="${ historyLocation }"> <strong>></strong> ${ historyItemTitle }</a>
</li>
historyItemPath
is the index of the entry in the history list, historyLocation
is the path of the page when it was visited, and historyItemTitle
is the title of the page, which is good to see when figuring out which page was visited. Note the data-action
, history#visitPageInHistory
, which Stimulus picks up when the html is changed on the page.
The Controller
The history_controller.js
Stimulus controller is responsible for listening for page changes, and recording the page into a history list with leavingPage()
. The controller then stores the list in the browser’s sessionStorage
, and the controller loads that list when it is initialized again. When connected to the page, the controller reads each entry, generates a list item, and inserts the history into the page. Then, if one of those history items is clicked, visitPageInHistory()
is called and the controller clears out history entries that were visited in the list after the selected entry.
The Code
The localSessionKey
is the key used store a JSON representation of the history array into sessionStorage
.
const localSessionKey = "history.history"
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "links" ]
initialize() {
// Listen for page changes
this.leavingPage = this.leavingPage.bind(this)
window.addEventListener("turbolinks:before-visit", this.leavingPage);
window.addEventListener("beforeunload", this.leavingPage);
// Load history from session store
let historyValue = window.sessionStorage.getItem(localSessionKey)
if ( historyValue ) {
this.history = JSON.parse(historyValue)
} else {
this.history = []
}
// Used to prevent current page from being entered into the history list when going back
this.recordVisit = true;
}
connect() {
// Load in the history and display it on the page
var links = ""
this.history.forEach( (historyEntry, historyLocation ) => {
links += linkHTML(historyEntry.title, historyEntry.path, historyLocation)
});
this.linksTarget.innerHTML = links;
}
disconnect() {
// Unload page change listener
window.removeEventListener("turbolinks:before-visit", this.leavingPage);
window.removeEventListener("beforeunload", this.leavingPage);
}
visitPageInHistory(event) {
// When going back to a page on our bread crumb list, remove items visited after desired page
let historyItemIndex = event.target.getAttribute('data-history-location');
this.history = this.history.slice(0, historyItemIndex);
this.recordVisit = false;
}
leavingPage(event) {
// Record page into history when leaving
if (this.recordVisit) {
let lastVisitedItem = this.history[this.history.length - 1];
if (lastVisitedItem == null || lastVisitedItem.path != this.pagePath ) {
this.history.push({ title: document.title, path: this.pagePath });
}
}
window.sessionStorage.setItem(localSessionKey, JSON.stringify(this.history));
}
get pagePath() {
return `${window.location.pathname}${window.location.search}`;
}
}
function linkHTML(historyItemTitle, historyItemPath, historyLocation) {
return `<li><a href="${ historyItemPath }" data-action="history#visitPageInHistory" data-history-location="${ historyLocation }"> <strong>></strong> ${ historyItemTitle }</a></li>`;
}
Better Web App Usability
Progressive Web Apps don’t require a lot more work than a regular web app. They only require realizing the subtle differences between the two and filling in any deficiencies. Adding breadcrumbs to a web page will enhance navigation on the site, especially more complicated sites with deep menus. Cutting down on clicks makes everyone happy.
Feel free to leave a comment or question below.
Want To Learn More?
Try out some more of my Stimulus.js Tutorials.