I'm making a single page webpage.
I've set up a node.js server and I've made it so it can determine whether or not a request from a user is a URL or a resource request.
When a user first joins I send them the index.html file which then requests css, images and javascript. (these are all skeleton files... e.g files that are the main layout of the website with links but not the actual content) On the client side javascript I have code that looks at the URL that the user is at, and then sends a request to the server for more html or css, all good so far.
However, when I update the URL using window.history.pushstate() (user clicks on <a>
tag, I have prevented default) it sends a request to the server to tell the user that they have changed URL, which I don't want. This is because the client side javascript should send resource requests so it can update the page without refreshing.
On my server, I don't believe there is a way to tell if the client already has the index.html, so when it recieves a URL request it doesn't know whether or not to send the index.html file (which causes a page refresh).
I was wondering if there was a way to either stop pushstate() from telling the server the user has changed url (as it is nt necessary) or for a way to know if a user already has the index.html file.
I know this should account for cases such as the user having the webpage open on multiple different tabs (and index.html should be send for each individual tab) or the url is manually changed.
If the server has to recieve the URL request my idea was using the remoteAddress and remotePort properties and storing those somehow and then comparing them when a user sends a request. (assuming that a new tab would use a different remotePort).
Sorry I couldn't include any code as I'm currently on my phone typing this so I don't forget this post in the morning. I'm not using express.js with node.js as I want to get comfortable knowing what is going on underneath before I use an abstraction module. I'm not using any other framework except javascript and jquery on the front end. Pure node on backend.
This isn't necessarily a code request, as I am comfortable writing my own, I just need advice on how I should go about this.
CodePudding user response:
Simple answer: actually call the function that calls push history.
Long Answer:
Found an answer on youtube https://www.youtube.com/watch?v=6BozpmSjk-Y
(git): https://github.com/dcode-youtube/single-page-app-vanilla-js
In essence, I needed to use routes in the client side javascript to solve my issue.
Client side index.js Code:
import Dashboard from "./views/dashboard.js";
import Posts from "./views/posts.js";
import PostView from "./views/postview.js";
import Settings from "./views/settings.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: "/posts", view: Posts },
{ path: "/posts/:id", view: PostView },
{ path: "/settings", view: Settings }
];
// Test each route for potential match
const potentialMatches = routes.map(route => {
return {
route: route,
result: location.pathname.match(pathToRegex(route.path))
};
});
let match = potentialMatches.find(potentialMatch => potentialMatch.result !== null);
if (!match) {
match = {
route: routes[0],
result: [location.pathname]
};
}
const view = new match.route.view(getParams(match));
let htmlr = await view.getHtml()
console.log(htmlr);
//document.querySelector("#app").innerHTML = htmlr;
};
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();
});
dashboard.js
import AbstractView from "./abstractview.js";
export default class extends AbstractView {
constructor(params) {
super(params);
this.setTitle("Dashboard");
}
async getHtml() {
console.log("Request to get HTML 2")
let request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
console.log(request.responseText);
document.querySelector("#app").innerHTML = request.responseText;
return request.responseText;
};
};
request.open("GET", "/frontend/static/pages/dashboard.html", true);
request.send();
}
}
Code is a bit spaghetti, but for now it works.