0

I'm working on Angela Yu's course on udemy, Drum Kit project In that JS file she uses var drum_note = this.innerHTML; inside of the anonymous function that's passed to addEventListener() as a listener.

I needed to know how that this works in this case, I knew the concepts like its used to point an Object, but how exactly it points towards the button.

My index.html

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title>Drum Kit</title>
  <link rel="stylesheet" href="styles.css">
  <link href="https://fonts.googleapis.com/css?family=Arvo" rel="stylesheet">

</head>

<body>

  <h1 id="title">Drum  Kit</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>

  <iframe src="https://open.spotify.com/embed/album/1CsEvbpeEVcek0pxt8SIv4" width="300" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>
  <footer>
    Made with ❤️ in London.
  </footer>
  <script src="index.js" charset="utf-8"></script>
</body>

</html>

My Index.js

buttons = document.querySelectorAll(".drum");

for (var i = 0; i < buttons.length; i++) {
  buttons[i].addEventListener("click", function() {

    var drum_note = this.innerHTML;
    switch (drum_note) {
      case "w":
        var tom1 = new Audio("sounds/tom-1.mp3");
        tom1.play();
        break;
      case "a":
        var tom2 = new Audio("sounds/tom-2.mp3");
        tom2.play();
        break;
      case "s":
        var tom3 = new Audio("sounds/tom-3.mp3");
        tom3.play();
        break;
      case "d":
        var tom4 = new Audio("sounds/tom-4.mp3");
        tom4.play();
        break;
      case "j":
        var kick_Bass = new Audio("sounds/kick-bass.mp3");
        kick_Bass.play();
        break;
      case "k":
        var crash = new Audio("sounds/crash.mp3");
        crash.play();
        break;
      case "l":
        var snare = new Audio("sounds/snare.mp3");
        snare.play();
        break;
      default:

    }
  })
}
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Aslam
  • 11
  • 1
  • 2
  • 1
    What is it you don't understand? `this` inside an event handler refers to the element the event was raised by. – Jamiec May 06 '21 at 12:51
  • Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Heretic Monkey May 06 '21 at 12:58
  • @Jamiec: It refers to the element the handler is bound to. – Felix Kling May 06 '21 at 13:21

5 Answers5

1

Additionally, you shouldn't rely on innerHTML to pass values as it may change in future which would break your code.

Use a data- attribute, then grab the value on click and use that as the filename, this way you don't have to add 4 lines for every sound.

document.querySelectorAll(".drum").forEach((el) => {
  el.addEventListener("click", function() {
    let note = this.dataset.note
    // new Audio(`sounds/${note}.mp3`).play();
    console.log(`playing: sounds/${note}.mp3`)
  })
})
<div class="set">
  <button class="drum" data-note="tom-1">w</button>
  <button class="drum" data-note="tom-2">a</button>
  <button class="drum" data-note="tom-3">s</button>
  <button class="drum" data-note="tom-4">d</button>
  <button class="drum" data-note="kick-bass">j</button>
  <button class="drum" data-note="crash">k</button>
  <button class="drum" data-note="snare">l</button>
</div>

You could obviously build it out and have data-instrument="drum" and query select on "[data-instrument]" then do:

new Audio(`sounds/${instrument}/${note}.mp3`).play();

Then have hundreds of different instruments without needing to add each of them to the switch.

Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106
0

"this" here refers to the specific button that you are going to click which will then play that specific sound.

Usaid
  • 13
  • 1
  • 6
0

this.innerHTML there points what is inside the button. In your case, it represents drum_notes like w,a,s,d,etc.

0

Typically, the JavaScript keyword this "refers back" to the object the function "belongs to". this can't be set (or overwritten) by assignment during execution, and the value for this will be different each time the function gets called (because it has a different owner).

In this case...

You created a button-event...for each button...in a set of buttons. As such, the value of this refers back to the HTML Element (which is a specific button). The HTML Element has a property called InnerHTML...which in this case...contains the corresponding letter for said button.

The case statement then keys-off the InnerHTML's text value & plays the correct note.

...Cheers!

Prisoner ZERO
  • 13,848
  • 21
  • 92
  • 137
  • *"the value for `this` will be different each time the function gets called"* Not if I all the function "on" the same object. – Felix Kling May 06 '21 at 13:23
  • I'm not saying that an extensive explanation of `this` is necessary. It just seems to be that this statement is more confusing than it is helping in understanding the nature of `this`. Because, to repeat my example, if I call the same method on the same object then `this` will refer to that same object. It's not suddenly a different object (e.g. a copy) just because I called the function. – Felix Kling May 06 '21 at 15:14
0

The value of this inside of a function solely depends on how this function is invoked, ie like a regular function, a method, or with this otherwise explicitly bound.

In the case you're asking, you're passing a callback function to addEventListener(), so addEventListener() has control on how to call it. It turns out that it actually binds the callback function local this variable to the object it itself is executed as a method on.

The code for addEventListener() probably goes something like this:

function addEventListener(eventName, callback){
  // Some code
  // Execute the callback, binding its local `this` variable to the addEventListener() own `this` - the Object.prototype.call() method allows to explicitly set the `this` execution context.
  callback.call(this, eventName)
}

Again, here, this will actually point to the HTML element addEventListener() is called as a method on. The callback is then called with its local this bound to this same element.

IAmDranged
  • 2,890
  • 1
  • 12
  • 6