Wes Bos has created a course to get you confident in your JS skills in 30 days. In it you work on a new project daily for 30 days and in each project you make something using vanilla JS without any frameworks or libraries.
Today I started the challenge and first project was to make a drum-kit where different sounds are played based on the keyboard button you press. And the corresponding button gets highlighted on screen.
The key concepts I learned from today's challenge were:
- Setting event-listeners with JS
- Working with keyboard events and identifying key-codes
- Playing audio and restarting the audio when it is already playing
- How to sync JS code with CSS transitions
- What value
this
takes inside the event listener function
Setting event-listeners with JS
The syntax for this is very simple
<target-element>.addEventListener('<event-name>', <function-to-call-when-event-is-fired>);
You can find a list of supported events here but beware, the complete list of events is too large and you might never need to use most of them.
Working with keyboard events and identifying key-codes
function handleKeyDownEvent(event) {
console.log(event.keyCode);
}
window.addEventListener('keydown', handleKeyDownEvent);
Here we attach an event-listener for the keydown
event to the window
object since we want to capture the events across our whole web-page.
Then we find out which key was pressed based on the keyCode
value passed via the fired event.
Wes has created a nifty little website (https://keycode.info) which we can use to find the corresponding keyCode
for any key.
Playing audio and restarting the audio when it is already playing
In this project we have a set of buttons on the screen corresponding to different keys on the keyboard. And each button has a corresponding <audio>
element on the page. The goal is to play the corresponding audio when a valid key is pressed on the keyboard.
So after detecting which key is pressed using the keyCode
attribute, we select the corresponding <audio>
element using a pre-specified data-key
attribute in HTML and call the audioElement.play();
method on it.
let audioElement = document.querySelector(`audio[data-key="${keyCode}"]`);
audioElement.currentTime = 0;
audioElement.play();
In the above code-block you might have observed that I set the current-time
of that element to 0
before calling play
on it. This is done because when an audio
is still playing (that is it has not finished) and we call .play()
on it, this new call gets ignored. Therefore we reset the play-time for that particular audio element so that it correctly registers and plays 2 consecutive commands of the same key.
How to sync JS code with CSS transitions
When a key is pressed, we highlight the corresponding button for a short duration on the screen while playing sound for it. To highlight the button, we just add a class playing
to it which scales it up and changes the border a bit. The second part of that is to remove the playing
class as soon as the transition is finished so as to return the button to normal state.
For this I initially thought of using setTimeout
with the same time-out as that mentioned in CSS as transition duration. Wes pointed out the flaw in it is that when we decide to update the transition duration we would need to make sure to update it in both the places (in CSS and in JS).
So a better way to synchronize CSS transitions with JS is again to use events. Here we specifically use transitionend
event on the buttons like below.
function removeButtonHighlight(event) {
if (event.propertyName === 'transform') {
this.classList.remove('playing');
}
}
document.querySelectorAll('.keys').forEach(button => {
button.addEventListener('transitionend', removeButtonHighlight);
});
In above code, we select each button and then add the transitionend
event-listener for it so now it will call removeButtonHighlight
only and exactly when any transition on any of those buttons ends.
After that we simply remove the playing
class from that button so that it returns back to its original/normal state. You might have noticed that I have put a check of event.propertyName === 'transform'
. I would suggest you try and remove that check and console.log
the event to find out why I might have put that check in.
What value this
takes inside the event-listener function
One of the toughest concept to learn and correctly understand in JS is this
or more specifically, what this
means at different points of execution in our code.
In the last code-block you have noticed that I simply did this.classList.remove('playing')
instead of trying to select that particular element using any other method.
It is so because in that particular case the value of this
is whatever object the function removeButtonHighlight
has been called.
So when we attach an event-listener on each of our button and on triggering of those events, they call removeButtonHighlight
with the context of that particular button. So there this
is referring directly to the button on which the event listener has been triggered.
Conclusion
Hope I have been able to explain what I learned in today's challenge. Hit me up on twitter @varun_barad in case you haven't understood something from this article or you have something new for me to learn.
In any case, don't forget to check out the JavaScript30 challenge from Wes Bos. It is an awesome way to get some JS confidence.