So I have a Single Page JavaScript web app that I've built from a tutorial HERE.
It's working well - I've integrated a few changes that people had in the Issues and Pull Requests on the GitHub link as well.
My links are also working well. If you click on a <a href> link, it redirects without reloading the whole page. AKA It's working as expected!
My problem comes when I want to use Javascript to change the page location. IE: As a result of a function/result of API call/something else, I want to be able to force the page to navigate to a different page without reloading the whole screen.
I'm hoping that I have all the required code below for someone to be able to help... Fingers crossed! Please let me know if I've missed something that you need to help.
My thought process goes along the lines of:
- The singlePageApp.js adds an event listener to the
<a href='' data-link>tags that callsnavigateTo - I just need to be able to somehow call that
navigateTofunction from somewhere else - I don't fully understand the code on that page enough to be able to make it available to the rest of the app because I can't just call
navigateTo('/whatever');from any script. It results in a function not defined error.
What have I tried? A lot - and I've forgotten what, but a few things
history.pushState(null, null, url);from a scriptnavigateTo('/whatever');from a scriptwindow.location.pathname = '/whatever'; router()<- This just results in a router not defined message
Other notes
- I'm not using react or other frameworks. Straight Javascript.
- I have jquery installed if it helps - Only reason is as a dependance on dataTables, but I'm not using it anywhere else.
Thank you VERY much in advance
Working links in a NAV bar
<a class="dropdown-item" href="#" data-link>List all services</a>
<a class="dropdown-item" href="/service/vm365/step1" data-link>Provision Service</a>
index.html
<script type="module" src="/static/js/singlePageApp.js"></script>
singlePageApp.js
import ServiceVM365Step1 from "./views/ServiceVM365Step1.js";
import ServiceVM365Step2 from "./views/ServiceVM365Step2.js";
import ServiceVM365Step3 from "./views/ServiceVM365Step3.js";
import ServiceVM365Step4 from "./views/ServiceVM365Step4.js";
import Dashboard from "./views/Dashboard.js";
const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
const getParams = match => {
const values = match.result.slice(1);
const keys = Array.from(match.route.path.matchAll(/:(\w+)/g)).map(result => result[1]);
return Object.fromEntries(keys.map((key, i) => {
return [key, values[i]];
}));
};
const navigateTo = url => {
history.pushState(null, null, url);
router();
};
const router = async () => {
const routes = [
{ path: "/", view: Dashboard },
{ path: "/service/vm365/step1", view: ServiceVM365Step1 },
{ path: "/service/vm365/step2", view: ServiceVM365Step2 },
{ path: "/service/vm365/step3", view: ServiceVM365Step3 },
{ path: "/service/vm365/step4", view: ServiceVM365Step4 }
];
// Test each route for potential match
const potentialMatches = routes.map(route => {
return {
route: route,
result: location.pathname.match(pathToRegex(route.path))
};
});
var match
potentialMatches.forEach(entry => {
if (entry.result != null) {
match = (match === undefined || entry.route.path.length > match.route.path.length) ? entry : match
}
})
if (!match) {
match = {
route: routes[0],
result: [location.pathname]
};
}
const view = new match.route.view(getParams(match));
document.querySelector("#content").innerHTML = await view.getHtml();
};
window.addEventListener("popstate", router);
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", e => {
if (e.target.matches("[data-link]")) {
e.preventDefault();
navigateTo(e.target.href);
}
});
router();
});
//////////////////////////////////////////////////////////////////
//// CODE BELOW WAS ME TRYING TO GET IT WORKING... It doesn't ////
var oldHref = document.location.href;
window.addEventListener("load", () => {
var bodyList = document.querySelector("body")
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (oldHref != document.location.href) {
oldHref = document.location.href;
//////////////////////////////////////////
//NOTE: This does get called when a <a href> link is clicked, but not when ONLY history.pushState is used to change the path from a script
console.error("CALLED NOW")
router();
}
});
});
var config = {
childList: true,
subtree: true
};
observer.observe(bodyList, config);
});