Mastering JavaScript: 30 Hands-on Projects for Beginners (Project Series 2/30)

Mastering JavaScript: 30 Hands-on Projects for Beginners (Project Series 2/30)

Understanding JavaScript fundamentals by building a Simple Counter Application

Introduction

This is the second article in the project series "Mastering JavaScript: 30 Hands-on Projects." In the first article, you read about the usefulness of JavaScript in frontend development and why you should start learning it as soon as you know the basics of HTML and CSS. We also built our first project in the series, a background colour changer, where we applied some simple JavaScript concepts.

The article series aims to build your mastery of the JavaScript language by building 30 simple projects. After building the projects, you'll feel confident enough to pick up any JavaScript framework.

Today, we'll be building Project 2 of the Project Series.

We'll be building a Simple Counter Application.

Project 2: Simple Counter Application

Working Project:

The goal of this project is to be able to increase, reset, and decrease the counter value of a counter. The interesting part is that some of the JavaScript concepts that were applied in the first project will also be used here, which means we are building on what you already know, which is a great way to learn Javascript.

New JavaScript concepts that'll be applied:

  1. Event Listeners

  2. Current Targets in an event

If you're not very conversant with these concepts, I've added links to video and article tutorials to the resources section of the article to bring you up to speed. You can read about them before you start this project

I have also provided the HTML and CSS codes which you can copy to your local machine so we can only focus on explaining the JavaScript logic.

HTML Codes:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Counter Wall</title>

    <!-- styles -->
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <main>
      <div class="container">
        <h1>Counter</h1>
        <span id="value">0</span>
        <div class="button-container">
          <button class="btn decrease">decrease</button>
          <button class="btn reset">reset</button>
          <button class="btn increase">increase</button>
        </div>
      </div>
    </main>
    <!-- javascript -->
    <script src="app.js"></script>
  </body>
</html>

CSS Codes:

/*
=============== 
Fonts
===============
*/
@import url("https://fonts.googleapis.com/css?family=Open+Sans|Roboto:400,700&display=swap");

/*
=============== 
Variables
===============
*/
:root {
  /* dark shades of primary color*/
  --clr-primary-1: hsl(205, 86%, 17%);
  --clr-primary-2: hsl(205, 77%, 27%);
  --clr-primary-3: hsl(205, 72%, 37%);
  --clr-primary-4: hsl(205, 63%, 48%);
  /* primary/main color */
  --clr-primary-5: hsl(205, 78%, 60%);
  /* lighter shades of primary color */
  --clr-primary-6: hsl(205, 89%, 70%);
  --clr-primary-7: hsl(205, 90%, 76%);
  --clr-primary-8: hsl(205, 86%, 81%);
  --clr-primary-9: hsl(205, 90%, 88%);
  --clr-primary-10: hsl(205, 100%, 96%);
  /* darkest grey - used for headings */
  --clr-grey-1: hsl(209, 61%, 16%);
  --clr-grey-2: hsl(211, 39%, 23%);
  --clr-grey-3: hsl(209, 34%, 30%);
  --clr-grey-4: hsl(209, 28%, 39%);
  /* grey used for paragraphs */
  --clr-grey-5: hsl(210, 22%, 49%);
  --clr-grey-6: hsl(209, 23%, 60%);
  --clr-grey-7: hsl(211, 27%, 70%);
  --clr-grey-8: hsl(210, 31%, 80%);
  --clr-grey-9: hsl(212, 33%, 89%);
  --clr-grey-10: hsl(210, 36%, 96%);
  --clr-white: #fff;
  --clr-red-dark: hsl(360, 67%, 44%);
  --clr-red-light: hsl(360, 71%, 66%);
  --clr-green-dark: hsl(125, 67%, 44%);
  --clr-green-light: hsl(125, 71%, 66%);
  --clr-black: #222;
  --ff-primary: "Roboto", sans-serif;
  --ff-secondary: "Open Sans", sans-serif;
  --transition: all 0.3s linear;
  --spacing: 0.1rem;
  --radius: 0.25rem;
  --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
  --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
  --max-width: 1170px;
  --fixed-width: 620px;
}
/*
=============== 
Global Styles
===============
*/
*,
::after,
::before {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: var(--ff-secondary);
  background: var(--clr-grey-10);
  color: var(--clr-grey-1);
  line-height: 1.5;
  font-size: 0.875rem;
}
ul {
  list-style-type: none;
}
a {
  text-decoration: none;
}
h1,
h2,
h3,
h4 {
  letter-spacing: var(--spacing);
  text-transform: capitalize;
  line-height: 1.25;
  margin-bottom: 0.75rem;
  font-family: var(--ff-primary);
}
h1 {
  font-size: 3rem;
}
h2 {
  font-size: 2rem;
}
h3 {
  font-size: 1.25rem;
}
h4 {
  font-size: 0.875rem;
}
p {
  margin-bottom: 1.25rem;
  color: var(--clr-grey-5);
}
@media screen and (min-width: 800px) {
  h1 {
    font-size: 4rem;
  }
  h2 {
    font-size: 2.5rem;
  }
  h3 {
    font-size: 1.75rem;
  }
  h4 {
    font-size: 1rem;
  }
  body {
    font-size: 1rem;
  }
  h1,
  h2,
  h3,
  h4 {
    line-height: 1;
  }
}
/*  global classes */

/* section */
.section {
  padding: 5rem 0;
}

.section-center {
  width: 90vw;
  margin: 0 auto;
  max-width: 1170px;
}
@media screen and (min-width: 992px) {
  .section-center {
    width: 95vw;
  }
}
main {
  min-height: 100vh;
  display: grid;
  place-items: center;
}
/*
=============== 
Counter
===============
*/
main {
  min-height: 100vh;
  display: grid;
  place-items: center;
}
.container {
  text-align: center;
}
#value {
  font-size: 6rem;
  font-weight: bold;
}
.btn {
  text-transform: uppercase;
  background: transparent;
  color: var(--clr-black);
  padding: 0.375rem 0.75rem;
  letter-spacing: var(--spacing);
  display: inline-block;
  transition: var(--transition);
  font-size: 0.875rem;
  border: 2px solid var(--clr-black);
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  border-radius: var(--radius);
  margin: 0.5rem;
}
.btn:hover {
  color: var(--clr-white);
  background: var(--clr-black);
}

Remember that the Javascript logic we are about to write should be written in app.js folder of the project.

The JavaScript Logic:

The first step is to declare a variable named counter and initialize it to 0. This variable will be used to keep track of the current value of the counter.

let counter = 0;

Next, we use the document.querySelector() method to select an element with the ID "value" and assign it to the variable valueDiv. This element will be used to display the current value of the counter. We'll also use the document.querySelectorAll() method to select all elements with the class "btn" and assign them to the variablebtns. This allows the script to add an event listener to all buttons on the page.

let counter = 0;
const valueDiv = document.querySelector('#value');
const btns = document.querySelectorAll('.btn');

Then, we use the forEach() method to loop through the btns variable and add an event listener to each button. The event listener will be triggered when the button is clicked.

let counter = 0;

const valueDiv = document.querySelector('#value');
const btns = document.querySelectorAll('.btn');

btns.forEach((btn) => {
  btn.addEventListener('click', (e) => {
    //code goes here;
  });
});

Our next step will be to implement that when the event listener is triggered, we will check the classList of the button that was clicked using the e.currentTarget.classList property. The e.currentTarget returns the element that triggered the event, in this case the button that was clicked. The classList property returns a DOMTokenList containing all the class names applied to that element. We will also save the class names in a variable called classes

let counter = 0;
const valueDiv = document.querySelector('#value');
const btns = document.querySelectorAll('.btn');

btns.forEach((btn) => {
  btn.addEventListener('click', (e) => {
    const classes = e.currentTarget.classList;
  });
});

Then, we will use an if-else statement to check if the button has the class "decrease" or "increase" and increments or decrements the counter variable accordingly. If the button does not have either of these classes, the counter variable is reset to 0.

let counter = 0;
const valueDiv = document.querySelector('#value');
const btns = document.querySelectorAll('.btn');

btns.forEach((btn) => {
  btn.addEventListener('click', (e) => {
    const classes = e.currentTarget.classList;
    if (classes.contains('decrease')) {
      counter--;
    } else if (classes.contains('increase')) {
      counter++;
    } else {
      counter = 0;
    }
  });
});

We will use another if-else statement to check the value of the counter variable. If the counter is less than 0, it changes the text colour of the valueDiv to red, if it's greater than 0, it changes the text colour of the valueDiv to green and if it's equal to 0, it changes the text colour to #333 (lighter shade of black).

let counter = 0;
const valueDiv = document.querySelector('#value');
const btns = document.querySelectorAll('.btn');

btns.forEach((btn) => {
  btn.addEventListener('click', (e) => {
    const classes = e.currentTarget.classList;
    if (classes.contains('decrease')) {
      counter--;
    } else if (classes.contains('increase')) {
      counter++;
    } else {
      counter = 0;
    }
    if (counter < 0) {
      valueDiv.style.color = 'red';
    } else if (counter > 0) {
      valueDiv.style.color = 'green';
    } else {
      valueDiv.style.color = '#333';
    }
  });
});

Finally, we will set the textContent of the valueDiv to the value of the counter variable, so that the current value of the counter is displayed on the page:

let counter = 0;

const valueDiv = document.querySelector('#value');
const btns = document.querySelectorAll('.btn');

btns.forEach((btn) => {
  btn.addEventListener('click', (e) => {
    const classes = e.currentTarget.classList;
    if (classes.contains('decrease')) {
      counter--;
    } else if (classes.contains('increase')) {
      counter++;
    } else {
      counter = 0;
    }
    if (counter < 0) {
      valueDiv.style.color = 'red';
    } else if (counter > 0) {
      valueDiv.style.color = 'green';
    } else {
      valueDiv.style.color = '#333';
    }

    valueDiv.textContent = counter;
  })
})

Ultimately, we just create a counter system that can be incremented, decremented, and reset with three buttons when clicked:

And we're done!

Short and sweet, init?

Conclusion

In this article, we created a simple counter application using JavaScript. The application allows the user to increment, decrement, and reset the counter value and updates the displayed value and its colour based on the current value. This project is a basic example of how to create a dynamic and interactive user interface with JavaScript.

This project can be a good starting point for more advanced projects and will help you build a solid foundation in JavaScript. With the knowledge gained from this article, you can build more complex projects and take your skills to the next level.

Watch out for Project 3/30 soon. Thanks for reading!

Resources

  1. MDN web docs: developer.mozilla.org/en-US/docs/Web/API/Ev..

  2. W3Schools: w3schools.com/jsref/event_currenttarget.asp

  3. CSS Tricks: css-tricks.com/almanac/properties/c/classlist

  4. jQuery documentation: api.jquery.com/event.currentTarget

  5. JavaScript.info: javascript.info/bubbling-and-capturing

  6. freeCodeCamp: guide.freecodecamp.org/javascript/event-lis..

  7. developer.mozilla.org/en-US/docs/Web/API/El..

  8. developer.mozilla.org/en-US/docs/Web/API/DO..

Please note that some of the tutorials may not cover all the concepts I listed, but it is a good place to start.

Credits

Special credit goes to John Smilger for using his projects in this article series.