A tutorial for advanced JavaScript DOM manipulation. We will use the Drum project to learn this section. Start by linking up the HTML with the CSS and JavaScript file.
In order to detect events on the page, we need to add event listener(s). We can create a function that will be called when a button is clicked.
// add a click event listener to the 1st button
document.querySelector('button').addEventListener('click', handleClick);
// what happen if we use the following line instead of the line above
// This will call the function right away when the page is loaded!!!
//document.querySelector('button').addEventListener('click', handleClick());
function handleClick() {
alert("I got clicked!");
}
Event listener is a tool that is waiting for something to happen. In the previous code, we pass the handleClick function as an input as to be called at the later time. The most common way to add the function is to pass anonymous function instead.
// using anonymous function
document.querySelector('button').addEventListener('click', function () {
alert('I got clicked here as well!');
});
Or you can use arrow function syntax.
// using arrow function
document.querySelector('button').addEventListener('click', ()=>{
alert('I got clicked here as well... again!');
});
To add event listener to all buttons, we can use loop.
// get all buttons by using 'drum' class
let items = document.querySelectorAll('.drum');
// looping the buttons to add event listener
for (let i=0; i<items.length; i++) {
items[i].addEventListener('click', ()=>{
console.log('button#'+(i+1)+' is clicked!');
});
}
In chrome, you can highlight a certain element in the web page then right-click > Inspect. Then you can use $0 to refer to that element in the console.
// display the highlight element
console.log($0);
// add event to the highlight element
$0.addEventListener('click',()=>{
console.log('click title');
});
In JavaScript, we can also pass a function as a paramenter of another function as well. You can try this code in the console.
// define function 'add'
add = (n1,n2)=>{
return n1+n2;
}
// define function 'mul'
mul = (n1,n2)=>{
return n1*+*n2;
}
// define function 'calculate' with function as 'operator' parameter
calculate = (n1,n2,operator)=>{
return operator(n1,n2);
}
calculate(3,5,add);
calculate(4,6,mul);
You can try debugging the calculate function by typing the following command in the console. This will bring us to the debugging tool.
// In the JavaScript console type:
debugger;
calculate(3,5,mul);
A high order functions are the functions that can take other functions as input paramenter. This feature is available in many modern computer languages.
Let's try adding a sound to our website. We can modify our function to load and play mp3 audio file.
let items = document.querySelectorAll('.drum');
for (let i=0; i<items.length; i++) {
items[i].addEventListener('click', ()=>{
console.log('button#'+(i+1)+' is clicked!');
// load and play mp3
let audio = new Audio('./sounds/tom-1.mp3');
audio.play();
});
}
This will create an HTML audio element. This element as many properties and methods, including the play() method.
Now we are going to add instrument images to our button using the CSS. We can add CSS background-image property to the .w .a .s .d .j .k .l with the value of "images/[.png]".
.w {
background-image: url("images/tom1.png");
}
.a {
background-image: url("images/tom2.png");
}
.s {
background-image: url("images/tom3.png");
}
.d {
background-image: url("images/tom4.png");
}
.j {
background-image: url("images/crash.png");
}
.k {
background-image: url("images/kick.png");
}
.l {
background-image: url("images/snare.png");
}
How do we know which button got clicked. We can use this to refer to the button that trigger the event. Try this code.
let items = document.querySelectorAll('.drum');
for (let i=0; i<items.length; i++) {
items[i].addEventListener('click', function() {
console.log('button#'+(i+1)+' is clicked!');
console.log(this.innerHTML);
});
}
Object can have multiple properties. Different object may have different value on the same set of properties.
let person1 = {
age: 12,
name: 'Jane',
hasPermit: true,
languages: ['Thai','Deutsch','Nederlands']
}
console.log(person1.name);
console.log(person1.languages[1]);
console.log(person1);
Instead of creating multiple objects of the same set of properties and initialize each and every property again and again. We can create a factory to help initialize objects. The factory is known as constructor function.
// the name is captitalized on the first letter of each word
function BellBoy (name, age, hasPermit, languages) {
this.name = name;
this.age = age;
this.hasPermit = hasPermit;
this.languages = languages;
this.moveStuff = function(stuff) {
console.log(this.name + ' move ' + stuff);
}
}
let bb1 = new BellBoy('Tim',19,true,['French','Thai']);
let bb2 = new BellBoy('Tom',29,true,['Thai','English']);
console.log(bb1);
console.log(bb2);
bb1.moveStuff('a table');
bb2.moveStuff('chair');
The output of the code would be:
BellBoy {name: "Tim", age: 19, hasPermit: true, languages: Array(2), moveStuff: ƒ}
BellBoy {name: "Tom", age: 29, hasPermit: true, languages: Array(2), moveStuff: ƒ}
Tim move a table
Tom move chair
Let's complete all event listeners for the buttons.
let items = document.querySelectorAll('.drum');
for (let i=0; i<items.length; i++) {
items[i].addEventListener('click', function() {
let label = this.innerHTML;
switch (label) {
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 crash = new Audio('./sounds/crash.mp3');
crash.play();
break;
case 'k':
let bass = new Audio('./sounds/kick-bass.mp3');
bass.play();
break;
case 'l':
let snare = new Audio('./sounds/snare.mp3');
snare.play();
break;
default:
console.log(this);
}
});
}
For listening to keyboard event, we can add the keydown event listener to the whole page, aka. document object. By passing event object to the function(), it is now possible to identify which key is pressed
// this time we pass the 'event' parameter to the function(event)
document.addEventListener('keydown', function(event) {
console.log(event);
});
If we press the a, b, alt keys, this will be the output display in the console.
KeyboardEvent {isTrusted: true, key: "a", code: "KeyA", location: 0, ctrlKey: false, …}
KeyboardEvent {isTrusted: true, key: "b", code: "KeyB", location: 0, ctrlKey: false, …}
KeyboardEvent {isTrusted: true, key: "Alt", code: "AltLeft", location: 1, ctrlKey: false, …}
We can see that there is a property key in the event object. Since the button click and the keyboard press work the same way. We can create another function makeSound(key) to do this work for both events.
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;
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 crash = new Audio('./sounds/crash.mp3');
crash.play();
break;
case 'k':
let bass = new Audio('./sounds/kick-bass.mp3');
bass.play();
break;
case 'l':
let snare = new Audio('./sounds/snare.mp3');
snare.play();
break;
default:
console.log(this);
}
}
Now we can change both event listeners as follows:
// Event listeners for buttons
let items = document.querySelectorAll('.drum');
for (let i=0; i<items.length; i++) {
items[i].addEventListener('click', function() {
let label = this.innerHTML;
makeSound(label);
});
}
// Event listeners for keyboard
document.addEventListener('keydown', function(e) {
makeSound(e.key);
});
The event object is usually referred to by the name e.
The function that is passed into the high order function is called the callback function. Callback function will be executed when an event happens.
We want to create buttonAnimation() so that we can activate the animation from both events.
function buttonAnimation(currentKey) {
let activeButton = document.querySelector('.'+ currentKey);
if (activeButton !== null) {
// add the CSS class .press to the button
activeButton.classList.add('pressed');
// remove .press class after 0.1 s
setTimeout( function() {
activeButton.classList.remove('pressed')
},100);
}
}