Creating Interactive Websites with JavaScript
(A How-To Guide)
Introduction
Opening up index.html for the first time in a project and seeing a blank white page is definitely daunting, but taking things one step at a time really helps me organize a work-flow that after practicing a few times has become repeatable, almost with my eyes closed....not really.
The Set Up
First, I look at the deliverables, or what actually is to be accomplished through my work - knowing where I am headed is the first step for figuring out a plan on how to get there. In order for me to do this, I usually keep a description of the functionality needed open on one screen, while at the same time I have open the blank index.html with my Developer Tools Console open to check my work in progress and splitting my main monitor with the code I am working on.
You talk to your computer ?
All the time. This is a quick description of what kind of conversations I have with it, asking how it can do what I want it to do as well as asking it what it needs from me in order to do that.
A Quick Side Note
I cannot emphasize enough the utility of using console.log(anything)
for testing everything. Whether it be making sure I'm grabbing the right HTML element or verifying that a function I'm writing has access to the all the variables I need it to in order to work I start by console logging to make sure it works.
Selecting HTML Elements for Ease of Use
In my experience, most often all the HTML elements and the CSS styling has already been put in place, leaving the only work for me to use JavaScript in order to make them interactive and work as intended.
Assigning Elements to Variables
Targeting elements using document.querySelector()
on either a class of elements (using .className) or a specific element (using #idTagName) and assigning them to a clear variable name makes keeping track of and adding elements or features such as eventListeners much easier in the long run.
(note: I find it helpful to add event listeners to static HTML elements here too)
Establishing Communication with the Server
This is where I usually make my first Fetch request just to see what kind of communication the server sends me. If my fetch request is to a database stored locally (shoutout json-server by typicode), my first fetch will look something like this: fetch('http://localhost:3000).then(res=>res.json()).then(console.log)
Thanks to my console.log, I know exactly what kind of info I am receiving back, and I can plan how to integrate it into where I trying to go based on the deliverables required of me.
In this case, thanks to my console.log, I can see exactly that the response to my fetch is an array of objects. and now I know exactly how to proceed and what I want the fetch's callback function to be.
Rendering The DOM
Here is where it becomes extremely important to keep track of functions and particularly the parameters that they take. In my experience, I have often been required here to make a JavaScript 'template' function that will be called many times as the response to my get request is iterated on. Depending on how much HTML is provided, I can either use rendering functions such as this to populate parts of a webpage or create entire elements altogether. I usually do this in a series of functions, starting with the function that accepts the response of the fetch and then iterates over it in the prescribed way (usually by passing individual elements to another function)
(Pro tip: if I think I'm going to want to access a specific element for a function, I've found that I like manually adding an attribute called dataset, and usually making an id attribute to it, and assigning it the value from the object the function is currently handling:
deleteButton.dataset.id = thingObject.id
this allows me to access that value later on in the event listeners section through target)
Event Listeners
Throughout rendering to the DOM, depending on the functionality desired, I will add Event Listeners for the desired functionality to the individual elements as they are being generated. Part of making event listeners is adding what happens at the event, and almost always I will make it a callback function: deleteButton.addEventListener('click', deleteThing)
When declaring the function, based on js knowledge, I know that a function called by an eventListener will have its first parameter be the actual event, so when building the event listener callback function, I start with console.logging the event:
function deleteThing(e)
{
console.log(e)
}
Examining the event in the Developer Tools console, I can find target, and drilling down to dataset, I can access the same id I added for each element in the Rendering the DOM section ! and now any server communication that I do as a result of these events will be targeted specifically to just that object !
(Pro Tip: that same id value in the dataset attribute is stored as a string, so parseInt() the id, otherwise it may not work as indexes for accessing objects in another fetch are often int's)
idInt = parseInt(e.target.dataset.id, 10)
Server Communication
Setting up
Up until this point, the only interaction made was a fetch to retrieve data, now armed with everything including the element id's, I can easily make my POST/PATCH/DELETE fetches!
I tend to put these in the functions that their respective event listeners call [i.e. deleteButton calls deleteThing(), likeButton calls likeThing()]. When making PATCH or POST requests, I construct the object that I will eventually stringify in the request early on in the function pulling together data from the event as well as some processing whether it be to add to an existing value of attribute or to add some data from a form if my function is acting from a form submit event.
Rendering the Changes from an Event
In my experience I have found that the .then() function can be extremely useful, because in the case of POST and PATCH fetches, the resolved promise returns the changed made to the server ! This is crucial for two reasons. First, anything done in a .then() by definition will only happen after successful updating of the server's data, and second, after processing using .json, (and a console.log to review the kind of contents returned) can be directly passed into a rendering function that is already made ! I put an example of this entire process below !
function submitNewObjectFromForm(e)
{
e.preventDefault()
let newObject =
{
firstAttribute: `${e.target.first.value}`,
secondAttribute: `${e.target.second.value}`
}
fetch('http://localhost:3000/objects',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'accept': 'application/json'
},
body: JSON.stringify(newObject)
})
.then(resp=>resp.json()).then(data=>renderObject(data))
}
At this point, by setting myself up for my next tasks, as well as breaking everything down into little steps, I was able to smoothly set up my basic framework and thought processes for making interactive webpages through communication with a rudimentary server !
TL:DR
When facing a challenge, break it down into steps ! These are the general steps I take to go from a blank index.html page to a fully functional website!
Top comments (0)