A New Vue On JavaScript30 - 01 JavaScript Drum Kit
Tags: anewvueonjs30, vue, javascript30, javascript
Published: 2018-08-03 04:00:26

---
title: A New Vue On JavaScript30 - 01 JavaScript Drum Kit
published: true
tags: ANewVueOnJS30,Vue,JavaScript30,JavaScript
canonical_url: https://davefollett.io/2018/08/01/a-new-vue-on-javascript30-01-javascript-drum-kit/
cover_image: https://davefollett.io/2018/08/01/a-new-vue-on-javascript30-01-javascript-drum-kit/js-drum-kit.jpg
series: A New Vue On JavaScript30
---

This article is part of the [A New Vue On JavaScript30](/t/anewvueonjs30) series that explores re-implementing [JavaScript30](https://JavaScript30.com) projects using [Vue](https://vuejs.org). Before reading, its a good idea to have:

- Completed JavaScript30’s 01 - JavaScript Drum Kit project
- Read the [A New Vue On JavaScript30 - Getting Started](/davefollett/a-new-vue-on-javascript30---getting-started-4o61) article

## Key Vue Concepts

The following Vue concepts are discussed in this article:

- `v-for` directive
- Class binding
- Event binding

## 01 - JavaScript Drum Kit

The first of the JavaScript30 projects walks through building a drum kit that binds key presses to playing drum sounds.  This one is a lot fun and I ended up creating two different Vue implementations:
* [index-VUE-V1.html](https://github.com/davefollett/JavaScript30/blob/master/01%20-%20JavaScript%20Drum%20Kit/index-VUE-V1.html)
* [index-VUE-V2.html](https://github.com/davefollett/JavaScript30/blob/master/01%20-%20JavaScript%20Drum%20Kit/index-VUE-V2.html)

Lets take a look at the first version I created.

## Vue Version 1

For the first approach, I decided to simply insert the code from the original project into their corresponding Vue locations:
* The HTML section fit inside the root <code>&lt;div&gt;</code>
* The functions went into the <code>methods</code> section
* The JavaScript that executed on page load was placed into the <code>mounted</code> function
* The <code>data</code>, <code>computed</code>, and <code>watch</code> sections were not needed so they were removed

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>JS Drum Kit</title>
  <link rel="stylesheet" href="style.css">
  <!-- Use Vue from a CDN -->
  <script src="https://unpkg.com/vue"></script>
</head>
<body>

<!-- Vue Root DOM Element -->
<div id="app">
  <div class="keys">
    <div data-key="65" class="key">
      <kbd>A</kbd>
      <span class="sound">clap</span>
    </div>
    <div data-key="83" class="key">
      <kbd>S</kbd>
      <span class="sound">hihat</span>
    </div>
    <div data-key="68" class="key">
      <kbd>D</kbd>
      <span class="sound">kick</span>
    </div>
    <div data-key="70" class="key">
      <kbd>F</kbd>
      <span class="sound">openhat</span>
    </div>
    <div data-key="71" class="key">
      <kbd>G</kbd>
      <span class="sound">boom</span>
    </div>
    <div data-key="72" class="key">
      <kbd>H</kbd>
      <span class="sound">ride</span>
    </div>
    <div data-key="74" class="key">
      <kbd>J</kbd>
      <span class="sound">snare</span>
    </div>
    <div data-key="75" class="key">
      <kbd>K</kbd>
      <span class="sound">tom</span>
    </div>
    <div data-key="76" class="key">
      <kbd>L</kbd>
      <span class="sound">tink</span>
    </div>
  </div>

  <audio data-key="65" src="sounds/clap.wav"></audio>
  <audio data-key="83" src="sounds/hihat.wav"></audio>
  <audio data-key="68" src="sounds/kick.wav"></audio>
  <audio data-key="70" src="sounds/openhat.wav"></audio>
  <audio data-key="71" src="sounds/boom.wav"></audio>
  <audio data-key="72" src="sounds/ride.wav"></audio>
  <audio data-key="74" src="sounds/snare.wav"></audio>
  <audio data-key="75" src="sounds/tom.wav"></audio>
  <audio data-key="76" src="sounds/tink.wav"></audio>
</div>

<!-- Vue Instance Section -->
<script>
  var app = new Vue({
    el: '#app',
    methods: {
      removeTransition: function (e) {
        if (e.propertyName !== 'transform') { return }
        e.target.classList.remove('playing')
      },
      playSound: function (e) {
        const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`)
        const key = document.querySelector(`div[data-key="${e.keyCode}"]`)
        if (!audio) { return }

        key.classList.add('playing')
        audio.currentTime = 0
        audio.play()
      }
    },
    mounted: function () {
      const keys = Array.from(document.querySelectorAll('.key'))
      keys.forEach(key => key.addEventListener('transitionend', this.removeTransition))
      window.addEventListener('keydown', this.playSound)
    }
  })
</script>

</body>
</html>
```

While this version works and was easy to make, I didn't feel as if it took advantage of Vue's features so I decided to iterate on it.

## Vue Version 2

When looking at the HTML section of the JavaScript30 project, you can see a repetitive pattern for each key: keyCode, keyName, and soundName.  By extracting those parts into a data structure, we can then make use of Vue's <code>v-for</code> directive to loop it and create the same HTML.  Lets first look at the data structure, an array of objects where each object represents a key:

```javascript
data: {
  keys: [
    {keyCode: 65, keyName: "A", soundName: "clap", audio: new Audio("sounds/clap.wav"), isPlaying: false},
    {keyCode: 83, keyName: "S", soundName: "hihat", audio: new Audio("sounds/hihat.wav"), isPlaying: false},
    {keyCode: 68, keyName: "D", soundName: "kick", audio: new Audio("sounds/kick.wav"), isPlaying: false},
    {keyCode: 70, keyName: "F", soundName: "openhat", audio: new Audio("sounds/openhat.wav"), isPlaying: false},
    {keyCode: 71, keyName: "G", soundName: "boom", audio: new Audio("sounds/boom.wav"), isPlaying: false},
    {keyCode: 72, keyName: "H", soundName: "ride", audio: new Audio("sounds/ride.wav"), isPlaying: false},
    {keyCode: 74, keyName: "J", soundName: "snare", audio: new Audio("sounds/snare.wav"), isPlaying: false},
    {keyCode: 75, keyName: "K", soundName: "tom", audio: new Audio("sounds/tom.wav"), isPlaying: false},
    {keyCode: 76, keyName: "L", soundName: "tink", audio: new Audio("sounds/tink.wav"), isPlaying: false},
  ]
},
```
In addition to including the keyCode, keyName, and soundName, I also added an [HTMLAudioElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement) for playing the audio and an <code>isPlaying</code> flag.  This allows us to remove the <code>&lt;audio&gt;</code> elements and simplify the logic for adding and removing the <code>playing</code> class which gets applied to the key div elements.  If you are new to Vue, this next section may look a bit complex but I'll do my best to break it down.

```html
<div id="app">
  <div class="keys">
    <div
      v-for="key in keys"
      :key="key.keyCode"
      :class="[key.isPlaying ? 'playing' : '', 'key']"
      @transitionend="removeTransition($event, key)"
    >
      <kbd>{{ key.keyName }}</kbd>
      <span class="sound">{{ key.soundName }}</span>
    </div>
  </div>
</div>
```

As you can see the using the <code>v-for</code> directive has shortened the HTML section significantly.  Now we are able to loop the <code>keys</code> array, placing its data values into their corresponding spots.  Vue recommends setting the <code>:key</code> attribute when using <code>v-for</code> and even though its not necessary for this code I went ahead and did it anyway.  For more information on the <code>:key</code> attribute you can read about it [here](https://vuejs.org/v2/guide/list.html#key).

On the next line down I'm binding classes to the <code>&lt;div&gt;</code> element.  This will always apply the <code>key</code> class, but will only apply the <code>playing</code> class when <code>key.isPlaying</code> is truthy.

Lastly in the loop, we set the <code>transitionend</code> event to call the <code>removeTransition()</code> method passing the <code>$event</code> and <code>key</code> objects.  Setting the <code>transitionend</code> event this way allows me to remove <code>querySelectorAll</code> call and the <code>addEventListener</code> loop from the mounted function.

Now all that is left are some small changes to the <code>removeTransition</code> and <code>playSound</code> methods control the <code>key.isPlaying</code> flag and to play the audio from the <code>keys</code> array.

## Putting It All Together

{% codesandbox yvyqm83lnz initialpath=01%20-%20JavaScript%20Drum%20Kit%2Findex-VUE-V2.html module=%2F01%20-%20JavaScript%20Drum%20Kit%2Findex-VUE-V2.html %}

By taking advantage of Vue's features I was able to make some nice improvements to the code.  Looping the data structure instead of the repetitive HTML makes the code easier to maintain and expand in the future.  New keys can be added by simply adding a new entry into the <code>keys</code> array.  If the HTML needs to change, it can be updated in one spot instead of each of the nine key <code>&lt;div&gt;</code> elements.  Some additional improvements were:
* Removal of all direct JavaScript DOM queries & manipulations
* Removal of all data attributes

I hope you enjoyed this article, feel free to message me with any questions, comments, or _corrections_.  All code presented within this series is available in [my fork](https://github.com/davefollett/JavaScript30) of the official [JavaScript30 GitHub repository](https://github.com/wesbos/JavaScript30) which is located at:
* https://github.com/davefollett/JavaScript30
  
## Up Next

Next in the [A New Vue On JavaScript30](/t/anewvueonjs30) series is:

{% link davefollett/a-new-vue-on-javascript30---02-js-and-css-clock-45l4 %}

 [A New Vue On JavaScript30 - 02 JS and CSS Clock](/davefollett/a-new-vue-on-javascript30---02-js-and-css-clock-45l4).