JavaScript30 - 바닐라 자바스크립트 Day1
1. 소스코드 - HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS Drum Kit</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 웹 페이지의 화면 표현에 관련된 부분 -->
<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>
<script>
// playing 함수는 키 입력을 받았을 때 호출할 함수이다
function playing(e){
// audio의 data-key속성값이 입력받은 키의 값인 앨리먼트를 가져와 audio 상수에 할당
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
// class가 key인 앨리먼트의 data-key속성값이 입력받은 키의 값인 앨리먼트를 가져와 key 상수에 할당
const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
// 만약 입력된 키가 정상적인(HTML코드에 태그로 등록된) 키가 아니라면 함수를 종료
if(!audio) return;
// 입력받은 키에 해당하는 audio태그의 src의 재생시간을 0으로 할당
// 재생시간을 0으로 할당하는 이유는 동일한 키의 연속 입력 시 먼저 입력된 소리가 끝날 때 까지 기다리지 않고 소리를 처음부터 다시 재생 -> 반복해서 입력 가능
audio.currentTime = 0;
// 입력받은 키에 해당하는 audio태그의 src 재생
audio.play();
// 화면에 해당 키가 눌렸는지 CSS로 변화를 주기 위해 입력받은 키에 해당하는 key 클래스를 가진 앨리먼트 class에 'playing'을 추가
key.classList.add('playing');
}
// 키를 눌렀을 때 화면 표현에 대한 처리를 하기위한 함수
function removeTransition(e){
// e.propertyName은 transition과 연관된 속성 이름이다.
// e.propertyName이 'transform'이 아니라면 함수 종료
if(e.propertyName !== 'transform') return;
console.log(e.propertyName);
// 변화가 끝나면(transitionend) 키가 눌렸을때 화면에 변화를 주기 위해 추가했던 class 'playing'을 제거해서 원래대로 변경
this.classList.remove('playing');
}
// class가 key인 태그들을 가져와 상수 keys에 할당
const keys = document.querySelectorAll('.key');
// keys 배열을 forEach 메서드를 이용해 배열의 앨리먼트 key들에 removeTransition함수를 실행하는 'transitionend'이벤트를 추가한다.
// 'transitionend'이벤트는 변화(css transition)가 끝났을 때 실행되는 이벤트이다.
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
// 키보드 버튼이 눌렸을 때 playing 함수를 실행
window.addEventListener('keydown', playing);
</script>
</body>
</html>
2. 주목할 만한 문법
2.1. window.addEventListener('이벤트명', 함수)
- window객체에 이벤트를 추가하는 메서드
2.1.1. 'keydown' 이벤트
- 키보드가 눌렸을 때 이벤트가 발생
// 키보드 버튼이 눌렸을 때 playing 함수를 실행
window.addEventListener('keydown', playing);
2.2. document.querySelector(`앨리먼트[속성 값="${백 틱 표현식}"]`);
- document.querySelector( ): 앨리먼트를 선택해서 앨리먼트값을 가져오는 메서드
- 앨리먼트[속성 = "값"]: 앨리먼트의 속성이 값인 앨리먼트를 선택
- ${백 틱 표현식}: 문자열 내에서 변수나 함수를 표현할 수 있는 표현식, python의 f-string과 비슷하다.
// audio의 data-key속성값이 입력받은 키의 값인 앨리먼트를 가져와 audio 상수에 할당
const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`);
// class가 key인 앨리먼트의 data-key속성값이 입력받은 키의 값인 앨리먼트를 가져와 key 상수에 할당
const key = document.querySelector(`.key[data-key="${e.keyCode}"]`);
2.3. 앨리먼트.classList.add('클래스명');
- 앨리먼트에 '클래스명' 클래스를 추가하는 메서드
// 화면에 해당 키가 눌렸는지 CSS로 변화를 주기 위해 입력받은 키에 해당하는 key 클래스를 가진 앨리먼트 class에 'playing'을 추가
key.classList.add('playing');
2.4. 앨리먼트.classList.remove('클래스명');
- 앨리먼트에 '클래스명' 클래스를 제거하는 메서드
// 변화가 끝나면(transitionend) 키가 눌렸을때 화면에 변화를 주기 위해 추가했던 class 'playing'을 제거해서 원래대로 변경
this.classList.remove('playing');
2.5. 배열.forEach(key => 배열.addEventListener('이벤트명', 함수));
- 배열의 요소 각각에 주어진 함수를 실행하는 메서드
// keys 배열을 forEach 메서드를 이용해 배열의 앨리먼트 key들에 removeTransition함수를 실행하는 'transitionend'이벤트를 추가한다.
// 'transitionend'이벤트는 변화(css transition)가 끝났을 때 실행되는 이벤트이다.
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
참고: developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
2.6. 앨리먼트.addEventListener('이벤트명', 함수)
- 앨리먼트에 이벤트를 추가하는 메서드
2.6.1. 'transitionend' 이벤트
- transition(전이)가 끝났을 때 이벤트가 발생
// keys 배열을 forEach 메서드를 이용해 배열의 앨리먼트 key들에 removeTransition함수를 실행하는 'transitionend'이벤트를 추가한다.
// 'transitionend'이벤트는 변화(css transition)가 끝났을 때 실행되는 이벤트이다.
keys.forEach(key => key.addEventListener('transitionend', removeTransition));
참고: www.w3schools.com/jsref/event_transition_propertyname.asp