this.#keyboardEl.addEventListener("mousedown", this.#onMouseDown);
document.addEventListener("mouseup", this.#onMouseUp);
마우스를 누를 때(mousedown)은 키보드 요소에서 eventListener를 걸지만, 마우스를 뗄 때는(mouseup) 눌렀던 그 자리에서만 떼는 것이 아닌 키보드 밖으로 나가서 어디에서든 뗄 수 있기 때문에 document에 eventListener를 건다.
주어진 선택자와 일치하는 요소를 찾을 때까지 자기 자신을 포함한 위쪽(부모 방향, root까지)으로 문서 트리를 순회하여 찾아낸다.
가장 가까운 조상 element, 또는 자기 자신을 반환하거나 없으면 null.
#onMouseDown(e) {
e.target.closest("div.key")?.classList.add("active");
}
#onMouseUp() {
this.#keyboardEl.querySelector(".active")?.classList.remove("active");
}
이번에도 optional chaining을 사용하여 코드를 간결하게 작성했다.
mousedown
target의 가장 가까운 div.key를 찾아 배경색이 어두워지는 .active를 추가한다.
mouseup
마우스를 떼는 순간 .active를 가지고 있는 요소에게서 .active class 이름을 제거한다.
마우스가 떼지는 순간 input이 입력되게 하기 위해서 mouseup 이벤트 리스너에 input 작업을 한다.
const keyEl = e.target.closest("div.key");
const isActive = !!keyEl?.classList.contains("active");
const val = keyEl?.dataset.val;
keyEl
div.key 가진 요소
isActive
mouseup 될 때 active한 키보드 요소
val
키보드 요소에서 data-val이 존재하는지 판별하기 위한 변수 (없는 값도 있어서)
* !!변수 에서 느낌표 두 개 처리를 해주는 것은 undefined나 null 값이 확실하게 false로 처리하기 위함이다. (확실한 type casting)
if (isActive && !!val && val !== "Space" && val !== "Backspace") {
  this.#inputEl.value += val;
}
if (isActive && val === "Space") {
  this.#inputEl.value += " ";
}
if (isActive && val === "Backspace") {
  this.#inputEl.value = this.#inputEl.value.slice(0, -1);
}
키보드가 active한 상태이고 값이 있어야지 동작하게 한다.
input 요소에 지금 클릭된 요소의 data-val을 추가한다. 원래 있던 값에 이어서 보여주기 위해 += 
Space
공백 한 칸 추가
Backspace
배열에서 맨 뒤의 값 삭제
강의에서는 동시에 입력되지 못하게 하는 처리를 해주었는데, 키보드 입력은 input 커서에서 벗어나면 입력되지 않고 마우스 입력을 하면 커서에서 벗어나져서 해당 처리를 해주지 않았다.