May 14th, 2021
A code newbie recently asked me for some help with a project. With an HTML and CSS web page, she wasn't sure how to leverage her JavaScript basics to make it interactive.
How to make a simple web app, built with HTML, CSS, and vanilla Javascript, respond to user interaction.
Variables and Event Handlers
Let's solve this by coding out an example: Cookies for Class.

We have some cookies. Let's say we have 15 cookies in total, and we want to divide them evenly among the class. We've prepared the cookies ahead of time, but we're not sure how many students will be attending lessons today. So, we need to adjust the number of cookies to divide evenly among the children, and set their cookie limit appropriately. In our example we cannot have fractional cookies (they're individually wrapped).
Total cookies = 15
Cookies per child = 15 / number of children attending
Example: 15 cookies / 4 children = 3 cookies per child
Available cookies = 15 / number of children attending * number of children attending
Example: 3 cookies per child * 4 children = 12 cookies available to this class
I want to focus on vanilla JavaScript in this article, so here are the HTML and CSS files already laid out.
Before we can alter an element, we need access to it. There are a variety of methods to query a DOM element. You can get an element by its tag name or id. I tend to use the generic document.querySelector for its versatility. Let's get some elements that we want to interact with and store them in constant variables. In this app, that would be our buttons. The user clicks on a button, and then cool stuff happens. We'll also set the max number of cookies to 15.
Now, let's think about the values we want to change when the user clicks on one of those buttons. Some values we want to set to an initial value, and some we want to access and alter after we have some more information. Thinking back to our goal: we want to change the value of the following:
number of children when the user adds a child
number of cookies when children are added
visible cookie elements
a variable to keep track of those visible cookies to remove them when eaten
inputs:
When a child is added, an input (the little slider that counts up to cookie fullness) is also added to the DOM. We won't know how many are present until the start button is clicked.
We need access to them in more than one function**:
onStart - in order to set the max value
getCookie - to increment the value each time the 'get cookie' button is clicked
getCookie buttons:
For the same reasons as above, the number of buttons will change based on the number of children present.
We need access to them in more than one function**:
onStart - to set an eventListener to respond to clicks
getCookie - to disable it when cookie fullness has been attained
** When more than one function in our javascript files needs access to a variable, we have to initiate the value outside of any particular function, because of scope. Put simply, any variables initiated between those { } curly braces, whether it be in a function, for loop, if statement, etc, can only be accessed inside those { } curly braces. So, if you want to access a variable in two separate functions - you'll have to declare it outside.
When you're first creating an application, don't worry if you're not sure about all the values you may need. You can adjust as you go. Generally, if it won't change - make it a "const". If it will, use "let".
What kind of interaction are we expecting in our application? Button clicks.
In our html file, I've already set some onclick event listeners to most of our buttons. I chose to set the click event listeners directly on these elements because it's common when utilizing frameworks, like Vue (@click) or React (onClick). We'll also use the addEventListener method to dynamically add button functionality in a moment.
Let's look at our onclick methods. We have three: onStart, restart and addChild. Let's begin with the last, because it's the first button our users will most likely interact with.
When the 'Add Children' button is clicked, it should:
increment the number of children
add DOM elements to represent that child
adjust the number of available cookies: both the variable and the DOM elements
In order to alter the appearance of our page, we set the innerHTML of the elements we want to change to a new string. The 'cookieString' variable is filled with as many cookie divs as cookies are available with a simple for loop. The loop runs as many times as available cookies and adds a cookie each time. The html representing a child doesn't need to be completely re-written, as we're only adding - not recalculating the number of available items.
There is a problem with our function. What happens if there are more children than cookies? In order to handle the possibility, let's add an if statement to check if the numberifKids is equal to our maxNumberofCookies. We'll alert the user to the situation, and call the onStart method. Notice I'm using backticks and a template literal in the alert. This makes our code more robust. If we ever want to change the maxNumberofCookies, we only have to change it in one place and everything will continue to work as expected - as opposed to using 15.
For our little app, we want to increment the value in the slider each time a child gets a cookie. It should stop once the child has gotten all cookies available to them, and they should not be able to get any more. We also want to hide our 'Add Children' button as that functionality should be disabled. We'll also hide our start button and replace it with the restart button.
Let's begin! First, toggle the hidden class on the buttons we want to hide or show. This sets each element's visibility, as you can see in the CSS file. Next, we'll use querySelectorAll to get all the slider and button elements we added to represent the children. This method will return a Node List of DOM elements, which we can iterate over with the forEach method. We'll use this to set the event handler for each 'Get a Cookie' button click and to set the max value of the sliders.
The forEach function takes each element in the list and, optionally, that element's index. In order to make sure the correct child gets their cookie, we'll pass that index into our next function.
Let's eat! Each time a child gets a cookie, we want to show that they are one step closer to being full, and remove a cookie from the cookie box. We do this by incrementing the value of the correct slider, using that index number we passed in. Note that the value is of type string, so we need to parse it into a number using parseInt. We'll use the remove method to remove a cookie div.
We also want to disable our cookie getting button once a child has eaten all cookies available to them. When all the cookies have been eaten, we'll let our user know by changing the innerHTML of the cookie box.
Once all the cookies have been eaten, this is how our page should look:

Notice I've included a restart button. In the restart function, you can go through each altered element and value and reset them. Feel free to do this on your own.
Here's the finished JS file.
I hope you enjoyed this article and found some useful content.
Happy coding!