How Did I Make this Website? Pt. 4
Originally Posted: Nov. 7, 2022
Last Edited: Nov. 7, 2022 10:22 PM EST
Author: Mr. Siefen
Code Repo: https://github.com/mrSiefen/htmlTutorials
How do I even make JS Files?
HTML files make up the content of our webpage and CSS controls the style of our content. JS (javascript) allows us to do programmatic things. Processing inputs, handling clicks, changing colors based on triggers, hiding objects, spawning new cards, connecting to outside sockets, are all things you need JS for. All of the JS I am going to teach here is called client-side JS. In the same way as our website is served to the user (client) from a host server, our JS can run on either side.
Client-side JS doesn't require a connection to a networked server. There is alot that can be accomplished from client-side JS. Scripts run in real time and do NOT need to be compiled like Arduino or Java programs. Client-side JS can also be written in our HTML file in <script> tags or in a separate file. I prefer this second method even for short simple scripts for organizational purposes.
To get started with part 4 make sure you have a complete part 3 package to start from. If you have been following along you should have an index.html file, an images and icons folder, a stylesheets folder with a style.css file, and a scripts folder with a blank index.js file. If any of these are missing please download them from the github repo in the part 3 folder. Below is a preview gif of what our final webpage will look after applying our first javascript changes.
Let's Examine the Full JS File
window.setInterval(updateHeader, 1000);
window.setInterval(rotateImage, 17);
window.setInterval(fadingHeader, 50);
var ang = 0;
var opacity = 1;
var opacityIncr = -0.01;
var elmoGif = document.getElementById('elmoGif');
var rHead = document.getElementById('rainbowHeader');
var fHead = document.getElementById('fadingHeader');
function updateHeader(){
var x = Math.floor(Math.random() * 256);
var y = Math.floor(Math.random() * 256);
var z = Math.floor(Math.random() * 256);
if(((x + y + z)/3) < 126){
rHead.style.color = "white";
}
else{
rHead.style.color = "black";
}
var bgColor = "rgb(" + x + "," + y + "," + z + ")";
console.log(bgColor);
rHead.style.background = bgColor;
}
function rotateImage(){
elmoGif.style.transform = "rotate(" + ang + "deg)";
ang++;
//console.log(ang);
}
function fadingHeader(){
if (opacity <= 0 && opacityIncr == -0.01){
opacityIncr = 0.01;
}
else if (opacity >= 1 && opacityIncr == 0.01){
opacityIncr = -0.01;
}
opacity += opacityIncr;
fHead.style.backgroundColor = "rgba(109,109,109," + opacity + ")";
}
This is the full updated code for our index.js file. This code will run whenever our webpage loads. It looks like a lot if you are unfamiliar with JS and that's okay!
Don't worry. We're gonna break it down into elements just like always! If you really are worried and want a crash course in JS in more detail I recommend CodeCademy's free lessons!
Let's Break it Down into Elements
Window Control Code:
window.setInterval(updateHeader, 1000);
window.setInterval(rotateImage, 17);
window.setInterval(fadingHeader, 50);
The power of javascript comes from it's ability to modify any of our HTML elements. While this could mean just our tagged elements, it applies to the webpage and window as a whole too. The HTML DOM (Document Object Model) can be accessed and modified on the fly with JS. The window refers in our code to the browser window that is viewing the document (html file).
The .setInterval function calls a specified function every X milliseconds and forces the browser to update the DOM. SetInterval takes 2 arguments in parenthetical format (meaning in a set of parenthesis). The first argument is the name of our function we want run once every interval. The second argument is our length in milliseconds between intervals.
In the code above I use the setInterval function 3 separate times. Each call of the setInterval function creates a new, separate loop. Each one of those loops has a different target function and an interval. This is for many reasons, but mainly it's an idea absorbed from video game design.
By creating timed loops we are in essence controlling the FPS an action will happen at. Our first function updateHeader will be called at an FPS of 1. Our second function rotateImage will be called at roughly 58 FPS. Our third function fadingHeader will be called at roughly 20 FPS.
To learn more about what functions are I'd recommend reading the Mozilla Developer Docs. You will get to see a practical application of functions in this lesson really soon though. In the shortest possible terms functions are chunks of code that we want to call on demand from other parts of the code.
Variable and Constant Declarations:
var ang = 0;
var opacity = 1;
var opacityIncr = -0.01;
Variables in programming help us store data to be retrieved later. Every programming language has variables (changing data storage) and constants (non-changing data storage). First lets look at the variables or vars in JS. Data comes in many, many types. JS however is not type-specific for its data storage. This is similar to Python!
If you are used to programming in Java, C++, C# or other strongly typed languages this can feel so backwards. I agree, and you aren't wrong, but there isn't any reason this methodology can't work even if it feels unnatural. The responsibility falls on the programmer to keep track of the types. I could create two vars, one a number like 7 and another the text "seven" and in JS add them together. The end result is "7seven" in JavaScript, in Java this code would never even get compiled successfully.
Getting back on track let's examine our specific variables from the code above. There are 3 variables that I'm using to store data about my angle and opacity for elements on the page. My ang variable is meant to store the current angle of the elmoGif. The opacity variable and the opacityIncr variable are used with the fadingHeader method.
const elmoGif = document.getElementById('elmoGif');
const rHead = document.getElementById('rainbowHeader');
const fHead = document.getElementById('fadingHeader');
As I previously said the power of JS is controlling the DOM of our webpage. The 3 constants above store the specified elements by their HTML id. Document just like window refers not to an element but the page as a whole, so we are getting an element from the document (page) by its id and saving it to a constant. The reason these are constants instead of variables is because I always want the data storage to be that specific value. If your img, h1 and h2 tags do not have matching IDs please fix this by looking at the part 3 or part 4 code repos on github.
updateHeader() :
function updateHeader(){
var x = Math.floor(Math.random() * 256);
var y = Math.floor(Math.random() * 256);
var z = Math.floor(Math.random() * 256);
if(((x + y + z)/3) < 126){
rHead.style.color = "white";
}
else{
rHead.style.color = "black";
}
var bgColor = "rgb(" + x + "," + y + "," + z + ")";
rHead.style.background = bgColor;
console.log(bgColor);
}
Our first function is designed to update the main header of our webpage. We want to achieve two main actions; change the background color to a random color and change the text to a readable color based on that new random color. Each time the updateHeader function is called those actions should be run one time. The function is called once per interval by our window.setInterval call.
var x = Math.floor(Math.random() * 256);
var y = Math.floor(Math.random() * 256);
var z = Math.floor(Math.random() * 256);
I know what you're probably thinking. "Why are these variables not declared in the beginning like the rest of them?" This is because variables and constants come with something called a scope. Scope means is this a local or global variable, or in regular terms, can this variable be seen just inside of this function or loop, or can it be accessed from anywhere within my code.
In this case we want our 3 x, y and z variables to be local only. These variables are created as new variables whenever the function is called, and then destroyed when the function exits, thus saving running memory for other things. The x, y and z variables don't need to persist outside of the function so this is perfect! All 3 variables value is set to a random value from 0-255. To learn more about the Math functions in JS you can read the javatpoint webpage to get a quick list of whats available.
updateHeader() cont:
if(((x + y + z)/3) < 126){
rHead.style.color = "white";
}
else{
rHead.style.color = "black";
}
Continuing down through our function we find a conditional If/Else statement. Conditionals are the heart of dynamic code and work the same way in almost any programming language. If statements compute the value of whatever is in the parenthesis down to a simple yes or no answer. If the answer is yes the code between the curly braces {} after the if is run. What happens if the answer is no? We run the code between the {} after the else.
So here we are trying to ask a math comparison question and get a yes or no answer. If the average value of X, Y and Z, aka (x +y+z)/3, is less than 126 (half brightness in RGB) then set the text to white. If the last statement was not true then set the text to black. The real world application of this is automatically changing text color to be readable no matter what the background color is set to.
var bgColor = "rgb(" + x + "," + y + "," + z + ")";
rHead.style.background = bgColor;
console.log(bgColor);
Now that we figured our our new random x, y and z values we need to create a css string. While JS modifies DOM elements, it can also edit CSS attributes of a DOM element. The caveat is that the attribute values for any style will always be a string. So we can embed our X, Y and Z values inside of the string in place of our RGB values. We then set the background style to our new string. Then we print that string to the log console as a new line.
The console is a debugging output for javascript. There are different levels of Verbosity for the console. In chrome you can find the console under developer tools (f12) or right click and inspect then look on the top bar for the console tab. Console.log will print a line to the console with the value of whatever is between the parenthesis. This can be text, numbers, objects, lists and more and is super helpful for debugging parts of your code.
rotateImage() :
function rotateImage(){
elmoGif.style.transform = "rotate(" + ang + "deg)";
ang++;
//console.log(ang);
}
RotateImage is a super simple function that is also fun! Just like how we control the background color, we can control any CSS attribute of an element. This applies to transform attributes as well. The main goal of the function is to set the angle of the elmoGif image once per interval. Each time the interval is run the angle should increase.
Just like our other style attributes the transform values must be a string. We take our global ang value and embed it into the string. Then we increment our ang with the ++. There are various shortcuts like this like --, +=, -=, *=, etc. Each of these assumes that you want to overwrite the value of the variable by adding, subtracting, etc the value by itself and some other value.
In practical terms in our code the variable ang will be updated every loop to be equal to whatever ang was previously plus 1. This will make our angle increase by one each interval. As the angle increases the image will rotate further and further. This can carry on forever in a loop and the image will just keep on rotating!
fadingHeader() :
function fadingHeader(){
if (opacity <= 0 && opacityIncr == -0.01){
opacityIncr = 0.01;
}
else if (opacity >= 1 && opacityIncr == 0.01){
opacityIncr = -0.01;
}
opacity += opacityIncr;
fHead.style.backgroundColor = "rgba(109,109,109," + opacity + ")";
}
Our final function is designed to update the h2 header of our webpage. We want to achieve one action; fade the background color up and down automatically. Each time the fadingHeader function is called the opacityIncr should be checked and then the opacity should be incremented by the opacityIncr value. The function is called once per interval by our window.setInterval call.
if (opacity <= 0 && opacityIncr == -0.01){
opacityIncr = 0.01;
}
else if (opacity >= 1 && opacityIncr == 0.01){
opacityIncr = -0.01;
}
To start we need to see if we are at the maximum or minimum ends of our opacity. OpacityIncr needs to flip signs when we hit one of those threshold values. Unlike our previous conditional this time we have an If and and Else If and NOT an Else. This means we have two different specific conditions we want to handle, and everything else is ignored.
In our conditional statements we also see the use of &&. && is known as a logical operator and it lets us combine condition statements within one conditional to get one lump answer. && means and, || means or and they can mean one or both statements have to be true to run the code in the {}. To learn more about logical operators check out the video below.
fadingHeader() cont:
opacity += opacityIncr;
fHead.style.backgroundColor = "rgba(109,109,109," + opacity + ")";
The last two lines of our fadingHeader function should make sense just from a quick glance now. We increment our opacity with += based on the opacityIncr value. If the opacityIncr is a positive value we are fading up, if the value is negative we are fading down. Then we set the background attribute with an RGBA value where a is replaced by the opacity value.
What's Next?
Now you can apply your JS to the rest of the elements on your page. You can try affecting the different style attributes besides color like font-size, padding, margins, position, etc. If you really are having alot of fun with the JS you should check out the examples here from programiz.com for extra practice. Don't give up even when it gets tough.
Okay... you can give up if there's a fire, and definetly call the fire service, don't send them an email.
If you missed part 1, part 2 and part 3, or want to read any of the other blog posts please click the links or use the navigation bar at the top of this page. See you next time!