13.2 SPA and JAM Stack
# Multi-Page Scripts
When building websites that are made up of multiple HTML files, you must bear in mind that, every time the browser loads a webpage it is replacing an old one. When the new page loads, it will be parsed by the browser and it will load any attached JS files, CSS files, fonts, and images.
It is likely that these other files will be in the browser's cache (not to be confused with the Cache API which is controlled by the developer). However, to run, they still need to be loaded into memory.
Any variables and functions that you had in your old JS file are being recreated.
Any values that were in your old variables ARE GONE.
This is why we need to use that DOMContentLoaded
event to call an init
method inside our namespace object. We need to run through the setup and add event listeners on every page load.
So, how does our script know which page has been loaded?
# Page Specific Code
There are many ways that you can figure out which page just loaded our script. You can look at the location
object to get the URL. But the simplest way to do it is to add a unique id
attribute to your <body>
element in each of your webpages.
<html>
<head></head>
<body id="home"></body>
</html>
2
3
4
Then you can use that in your code as the identifier for each page.
const APP = {
init: () => {
//any page has loaded
APP.addListeners();
APP.pageSpecific();
},
pageSpecific: () => {
//run code that is specific to each page
let id = document.body.id;
if (!id) return; //page is missing an id
switch (id) {
case 'home':
//on the home page
break;
case 'other':
//some other page
break;
case 'contact':
//on the contact page
break;
default:
//page that we don't recognize the id
//or page with no specific code needed
}
},
addListeners: () => {
//load any event listeners that are common to all pages
//like hashchange, popstate, main nav clicks, etc
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# CSS and Style Implications
If you are adding unique ids to each page's <body>
tag then you can leverage that in your CSS.
A common use is to style current links or hide and show page specific elements.
#home .sidebar {
display: none;
/* on the home page hide the sidebar */
}
#products .footer {
position: fixed;
width: 100vw;
bottom: 0;
height: 6rem;
/* make the footer fixed on the products page */
}
2
3
4
5
6
7
8
9
10
11
# SPA
A Single Page Application (SPA)
is what the name implies. A single HTML file that gets loaded to the browser and then the JavaScript is the Application logic responsible for showing and hiding and loading new content. We can use the History API
to manage the location bar and history array. We use the Fetch API
to get new content from the server. We can use the Cache API
and the Web Storage API
to save items, like images or JSON files, loaded from the server, which will improve performance in your app.
# JAMStack
The JAM
in JAMStack
refers to JavaScript, APIs, and Markup. Basically what we have been doing since week 5. A JAMStack
application can be a SPA
or it can be a multi-page web app. It uses all the same HTML5 APIs as a SPA site - history api
, fetch api
, cache api
, web storage api
and more.
The defining feature of a JAMStack
site is that all the files used to make the site are static ones. There is no server-side programming required, with the exception of the APIs. Generally, the APIs are built independently from the website. They can be hosted elsewhere and use any kind of server-side programming language.
You can even use Github Pages
to host and run a JAMStack website. The dynamic content will come from one or more APIs and be fed into your static website using client-side JavaScript.
# Server-Side Programming, Servers, Hosting & Databases
There are so many different programming languages in use today that it is virtually impossible to keep up with all of them. There are also many different kinds of web servers. Some webservers are able to support multiple programming languages, some support only one. On top of all that there are tons of options for different types of databases.
There are many kinds of hosting choices available to you and the one that you pick will be directly connected to which programming language and which kind of database you want to use.
# Languages
Here are a few of the server-side programming languages that you could use to build websites.
- NodeJS
- PHP
- Ruby
- ASP.Net C#
- Python
- Java J2EE
Most of those come with a variety of frameworks that can be used with the language to make development faster.
Next semester we will be leveraging your knowledge of JavaScript to create websites and APIs with NodeJS
.
# Databases
There are different types of Databases, each with its own strengths and weaknesses. Here are the main categories with a few examples, in brackets, of their implementations.
- Text Files (
txt
,csv
,xlsx
) - Relational Databases (
MySQL
,MariaDB
,PostgreSQL
,SQL Server
,Oracle
) - Document Databases (
MongoDB
,DynamoDB
,DocumentDB
) - Graph Databases (
Neo4j
) - Hierarchical Databases
Each type has its own learning curve and mental models that you need to figure out.
In our program we will be focused on Document Databases but will give you a brief introduction to Relational Databases too.
The database is where you save the data that you want to save with multiple users. It is where APIs get the information that they are sharing with webpages.
# Window.open()
If you ever need to open a new window or tab from JavaScript, you can do this with the window.open()
method. The decision to open a new tab or window depends on the settings in the user's browser. It is not something we can control with JS. There is also a window.close()
method.
When you use JavaScript to open a new window/tab you will be returned a reference to the newly created window
object. This means that you can control what is happening in the page that you opened from the creating page script.
let url = 'https://www.mydomain.com';
let features =
'menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=no';
let winRef = window.open(url, '', features);
2
3
4
The features parameter controls which elements will be visible if you are opening a new window.
If the domain for the page that you are opening is the same as the domain for the page that is running your script, then you will have full control over it. If the domain is different then there will be some security limitations to what you can control.
//to close the a window or tab that was opened by JS from within that page
window.close();
//to close the newly opened window from the script that opened it
winRef.close(); //winRef is the variable created in the script above
2
3
4
5
# Window Loading and Unloading
When you are loading and unloading pages there are a series of event that take place. They will always take place in the same order.
This concept applies to many of the events that you use in your code. A single user action results in a cascade of events. A user taps on theA
key on their keyboard -> at the very least there will be a keydown
, keyup
, and keypress
event sequence. However, depending on what element had focus there could also be an input
event or a propertychange
event. These events all have a predefined sequence that they have to follow.
When a page is loading these are the events that could be triggered in the order that they would occur.
window.readystatechange
window.DOMContentLoaded
window.load
window.pageshow
window.popstate
window.hashchange
When the user clicks on a link to navigate away from the page, these are the events that will be triggered, in the order that they will occur.
element.mousedown
element.focus
element.mouseup
element.click
window.pagehide
window.beforeunload
window.unload
And, if a page loses focus by a user switching tabs or applications.
window.visibilitychange
If you switch away from a page by moving to another tab or, on a mobile device, opening another app, then you will trigger the window.visibilitychange
event. Switching back to the tab will also trigger the visibilitychange
event. The pagehide
event will not be triggered by leaving the tab or opening another app. The beforeunload
and unload
events are not as reliable for running code. Usually, by the time those events happen the process of leaving is already underway. If you want to send a fetch()
call to upload recent data then the pagehide
or beforeunload
can be used. The unload
event cannot be used for fetch
because the page will be gone before the message is sent. Even with beforeunload
you can send data but you will not receive a result before the page is unloaded.
Something that might be of use to you at some point is the persisted
property, of the pageshow
and pagehide
events, which has a boolean value indicating whether or not the page was loaded from the browser cache.
window.addEventListener('pageshow', (ev) => {
if (ev.persisted) {
//page was loaded from browser cache
} else {
//page was NOT loaded from browser cache
}
});
2
3
4
5
6
7
This might be useful if you decide you want to check for new versions of files or use your own files from the Cache API
.
# What to do this week
TODO
Things to do before next week.
- Read all the content from
Modules 13.1, 13.2, and 14.1
.