Complete 2024 Web Development Bootcamp

Dr. Angela Yu

Back to JavaScript Index Page


Drum Roll
Code Breakdown
Intro

The Drum Roll app is an exercise in adding eventListener's for both a mouse click, and also a keypress, to accomplish the same output. We are also going to see how to play sounds with JavaScript. The finished app can be found here.

To start we need our sound and image files for the various drum components. These can be found in the following folders:

  • folder icon  images
    • crash.png
    • kick.png
    • snare.png
    • tom1.png
    • tom2.png
    • tom3.png
    • tom4.png
  • folder icon  sounds
    • crash.mp3
    • kick.mp3
    • snare.mp3
    • tom1.mp3
    • tom2.mp3
    • tom3.mp3
    • tom4.mp3




The HTML

Now, the basic HTML is pretty simple and the complete code will be listed below, but basically, we're going to set up a <div> container and fill it with buttons that represent the different drum kit components.

  • <div class="set">
    • <button class="w drum">w</button>
    • <button class="a drum">a</button>
    • <button class="s drum">s</button>
    • <button class="d drum">d</button>
    • <button class="j drum">j</button>
    • <button class="k drum">k</button>
    • <button class="l drum">l</button>
  • </div>
So our container has a class of set. But notice how each drum component has two classes assigned, a letter and drum.

The drum class is used by the JavaScript to identify that a drum component has been clicked. This is controlled with a "click" eventListener.

The letter class allows for CSS styling to produce the proper drum image for each letter. So each drum image is produced in the CSS and is controlled by the letter that identifies each drum component.

The letter is also assigned as a label, to indicate which key the user needs to press to press to get the drum sound. These are controlled with a "keydown" eventListener.

The scheme is that you can either "click" a drum component image to get a sound, or you can "press" the corresponding key to get the sound.




The JavaScript

For the JavaScript, the first thing we should do is to create a "click" eventListener to see if a drum image was clicked on. Since we gave all of the buttons a class of .drum, we can use a for loop to add a "click" eventListener to each drum component. From there we can use the keyword this to determine which drum image was actually clicked.

  • The "click" eventListener
  • // loop through all of the HTML elements that have a class of drum
  • for ( let i = 0; i < document.querySelectorAll('.drum').length; i++) {
    •  
    • // add a "click" eventListener to each of these items
    • document.querySelectorAll('.drum')[i].addEventListener('click', function () {
      •  
      • // add the innerHTML (this is the HTML button label), to a variable
      • let buttonInnerHTML = this.innerHTML;
      •  
      • // run the function to produce the sound
      • makeSound(buttonInnerHTML);
    • });
  • }

So we're going to run through all of the HTML elements that have a class of .drum. In our app this will be all of the HTML Buttons that represent a drum component. And for each element:

  • attach a "click" eventListener to it
  • take the letter, (this is the button label, (innerHTML)) that represents this drum, and assign it to the variable buttonInnerHTML
    • in this instance, this refers to the individual item in our for loop
  • call the makeSound() function, passing in our variable (buttonInnerHTML), to actually produce the appropriate drum sound

Note that we are using this to represent each individual item as we progress through the for loop.


And next we should create our eventListener for our key presses.

  • The "keydown" eventListener
  • document.addEventListener('keydown', (evt) => {
    • makeSound(evt.key);
  • });

This is pretty simple and straight forward:

  • We are adding a keydown eventListener that is checking the entire document for a key "down" press.
  • We are passing in an argument (evt) that is used to determine which key is pressed
    • - if we console.log(evt) we can get all kinds of information on the key that was pressed
    • - one of these properties is key which tells us exactly which key was pressed
  • finally we call the makeSound() function, passing in the data, (evt.key) for which key was pressed.


So whether we click on a drum image, or press the appropriate key, we should end up with the same variable being passed into the makeSound() function. This variable will be a letter that either comes from the button innerHTML or from the keypress which produces evt.key.

And now that we have our letter that we can pass in to our makeSound() function, let's go ahead and build the function.




The makeSound() function

So we're building a function that is going to have one argument passed into it. This is the letter that represents which drum component was selected, either by a mouse click, or by a key press. And because we a trying to process a letter, the Switch/Case method is the best way to go.

The switch statement is used to execute different blocks of code depending on the value of an expression (in our case, the letter that is being passed into the function).

The switch expression is then evaluated, and if it matches the value of one of the case labels, the code block associated with that case is executed. If none of the case labels match the value of the expression, the code block associated with the default clause is executed.

So our makeSound() function will look something like this:

  • function makeSound (key) {
    • switch (key) {
      • case "w":
        • let tom1 = new Audio(`./sounds/tom-1.mp3`);
        • tom1.play();
        • break;
      • case "a":
        • let tom2 = new Audio(`./sounds/tom-2.mp3`);
        • tom2.play();
        • break;
      • ...
      • ...
      • default:
        • alert(`Incorrect Key "${key}" Pressed!!`);
    • }
  • }
Let's break this down.

First off, we're only showing the code for the first two case match possibilities. Remember, our letters that will be passed into the function are limited to: w, a, s, d, j, k, and l. We've shown th code for w, and a, and the rest of the case code blocks will be the same except for the variable names and the mp3 that is being called.

So we are passing in our "key" letter and processing it through the case code blocks. If the letter passed in is NOT one of our letters, (w, a, s, d, j, k, l) then the default code block runs and in this case, just identifies that a key outside of our range was pressed.

If the key matches one of our case conditions that that code block will run and play the appropriate mp3 file. Lets break that code down:

  • let tom1 = new Audio(`./sounds/tom-1.mp3`);
  • tom1.play();
  • break;

Umm, what is this new keyword and what does it mean? Well, new is signifying that this is a "High Function", also known as a "Higher-order Function".

Higher-order functions in JavaScript are functions that can accept other functions as arguments or return functions as output.

In our case, the new Audio (Higher-order function) is selecting a file path and then outputting the play() function, to play the audio file specified in the path statement.

So we are using a Higher-order Function get a filepath to a file, assign it to a variable, and then output the file using the play() function.

The break statement at the end of each case code block simple exits out of the switch statement.




Drum Animation

We should have everything running nicely now, but let's add one more piece. How about when we select a drum either by mouse click, or by key press, let's add some type of animation that gives a visual cue that the drum was selected.

In order to accomplish this, we're going to create the following function:

  • function buttonAnimation (currentKey) {
    • let activeButton = document.querySelector("." + currentKey);
    • activeButton.classList.add("pressed");
    • setTimeout(function() {
      • activeButton.classList.remove("pressed");
    • }, 175);
  • }

So we've created a function that is going to have one variable passed in to it. This variable will be the letter of the drum component that was selected, (w, a, s, d, j, k, l).

We're going to use classList to add our animation. classList can return all of the classes assigned to an HTML element. But it has some interesting properties such as:

  • add - adds a new class to an HTML element
  • remove - removes a class from an HTML element.

Since we're working with classes, we need to convert our variable that is passed into the function, (currentKey), into a class. Our second line of code does that by adding a dot (.) before the letter that was passed in. So activeButton becomes a class name that represents the drum component that was selected.

So if you look at the drum components, you see the letters are a blue color with a yellow glow effect around them. We already have a class that defines that.

But our next line of code:

  • activeButton.classList.add("pressed");
is adding a NEW class (pressed) to this HTML element only. So all we have to do is write a new class rule for the class pressed. In our case we are simply going to change the blue text color to red. In the css file:
  • css
  • .pressed {
    • color: #da0404;
  • }

Now we don't want the text to stay red, so we've added a timeout function, and after 175ms, we will remove the class from the element with the line of code:

  • activeButton.classList.remove("pressed");

Now we should understand that we are not creating a new class with classList.add. The class pressed is created in our CSS stylesheet. All we are doing is adding, and then removing the class, to one single HTML element.

and finally

We need to call this function. And the place to call it is right after we call our makeSound() function so that whether we select a drum component with a mouse click, or with a key press, we still get the visual animation.

So we need to call the function from two places:

  • Inside the "click" event:
  • after: makeSound(buttonInnerHTML);
  • add: buttonAnimation(buttonInnerHTML);
and
  • Inside the "keypress" event:
  • after: makeSound(evt.key);
  • add: buttonAnimation(evt.key);




The Final Code
  • HTML
  • // the HTML Code
  • <div class="container">
    • <h1 id="title">Drum 🥁 Roll</h1>
    • <div class="set">
      • <button class="w drum">w</button
      • <button class="a drum">a</button
      • <button class="s drum">s</button
      • <button class="d drum">d</button
      • <button class="j drum">j</button
      • <button class="k drum">k</button
      • <button class="l drum">l</button
    • </div
  • </div

 

  • JavaScript
  • // the JavaScript Code
  •  
  • The "click" eventListener
  • // loop through all of the HTML elements that have a class of drum
  • for ( let i = 0; i < document.querySelectorAll('.drum').length; i++) {
    •  
    • // add a "click" eventListener to each of these items
    • document.querySelectorAll('.drum')[i].addEventListener('click', function () {
      •  
      • // add the innerHTML (this is the HTML button label), to a variable
      • let buttonInnerHTML = this.innerHTML;
      •  
      • // run the function to produce the sound
      • makeSound(buttonInnerHTML);
      • // add the text animation
      • buttonAnimation(buttonInnerHTML);
    • });
  • }
  •  
  • The "keydown" eventListener
  • document.addEventListener('keydown', (evt) => {
    • // run the function to produce the sound
    • makeSound(evt.key);
    • // add the text animation
    • buttonAnimation(evt.key);
  • });
  •  
  • The makeSound() function
  • function makeSound (key) {
  • // add the text animation
    • switch (key) {
      • case "w":
        • let tom1 = new Audio(`./sounds/tom-1.mp3`);
        • tom1.play();
        • break;
      • case "a":
        • let tom2 = new Audio(`./sounds/tom-2.mp3`);
        • tom2.play();
        • break;
      • case "s":
        • let tom3 = new Audio(`./sounds/tom-3.mp3`);
        • tom3.play();
        • break;
      • case "d":
        • let tom4 = new Audio(`./sounds/tom-4.mp3`);
        • tom4.play();
        • break;
      • case "j":
        • let snare = new Audio(`./sounds/snare.mp3`);
        • snare.play();
        • break;
      • case "k":
        • let crash = new Audio(`./sounds/crash.mp3`);
        • crash.play();
        • break;
      • case "l":
        • let kick = new Audio(`./sounds/kick.mp3`);
        • kick.play();
        • break;
      • default:
        • alert(`Incorrect Key "${key}" Pressed!!`);
    • }
  • }

Download Complete Code

zip file icon   drumRoll.zip

 


Back to Top