
40.1 이벤트 드리븐 프로그래밍
클릭, 키보드 입력, 마우스 이동 등의 일이 일어나면 → 감지 → 특정한 타입의 이벤트 발생
이 때, 이벤트가 발생하면 호출할 함수를 이벤트 핸들러라고 함!
그리고 이벤트가 언제 발생할지 몰라서 개발자가 명시적으로 함수를 호출하지 못함 → 브라우저한테 ‘너가 처리해’(= 함수 호출 위임)라 하는 게 이벤트 핸들러 등록
이런식으로 이벤트를 중심으로 제어하는 프로그래밍 방식 == 이벤트 드리븐 프로그래밍
40.2 이벤트 타입
= 이벤트의 종류를 나타내는 문자열 (예를 들어, click, mouseenter….)
약 200개 정도가 있고 머가 있는지는 너무 많아서 책 참고하셔서 그때 그때 보시면 될듯요
마우스 이벤트(click, mouseup, mousedown, mouseleave…등등)
키보드 이벤트(keydown, keypress, keyup등등…)
엄청 많습니다…
40.3 이벤트 핸들러 등록
이벤트가 발생하면 브라우저에 의해 호출될 함수 = 이벤트 핸들러
이걸 등록하는 방법은 세가지
(1) 이벤트 핸들러 어트리뷰트 방식
쉽게 말하면 onclick과 같이 on + 이벤트 종류를 통해 등록하는 것
<button onclick="sayHi()">Click me</button>
<script>
function sayHi() {
console.log('Hi')
}
</script>
이때 이벤트 핸들러 어트리뷰트 값으로는 함수 참조가 아닌, 함수 호출문같은걸 할당함
→ 뒤에 들어가는 sayHi() 이 값이 JS코드로 해석되어서 실행됨 그래서 이렇게도 쓸 수 있음
<button onclick="console.log(1); console.log(2);">Click me</button>
근데 이방식은 쓰지말라고는 함 왜냐면
- HTML, JavaScript를 완전히 분리된 "관심사"로 보았습니다
- 따라서 HTML에 직접 JavaScript를 넣는 이벤트 핸들러 어트리뷰트 방식을 지양했죠
하지만 컴포넌트 기반의 개발을 하는 리액트나 뷰와 같은 라이브러리/프레임워크에서는
- 컴포넌트라는 하나의 단위 안에 HTML, CSS, JavaScript가 모두 포함됨
- 이들을 분리된 관심사가 아닌, "하나의 컴포넌트를 구성하는 요소들"로 봄
- 따라서 이벤트 핸들러를 HTML과 비슷한 방식으로 직접 작성하는 것이 자연스러움
라는 이유로 이벤트 핸들러 어트리뷰트 방식과 비슷하게 쓴다고 하네요
(즉, CBD에서는 "버튼 컴포넌트"라는 하나의 완성된 단위를 만들 때 HTML 구조, 스타일링, 이벤트 처리가 모두 그 컴포넌트의 본질적인 부분이라고 보기 때문에, 이벤트 핸들러 어트리뷰트와 비슷한 방식을 사용하는 것이 오히려 더 적절하다는 의미입니다.)
리액트일때..
<button onClick={sayHi}>Click me</button>
뷰일때..
<button v-on:click="sayHi($event)">click</button>
(2) 이벤트 핸들러 프로퍼티 방식
이 방식도 위와 동일하게 onclick과 같이 on + 이벤트 종류로 이루어짐
<button>Click me</button>
<script>
const $button = document.querySelector('button')
$button.onclick = function () {
console.log('Hi')
}
</script>
이런식으로 이벤트를 발생시킬 객체인 이벤트 타깃, 이벤트 종류를 나타내는 이벤트 타입, 그리고 이벤트 핸들러를 지정해서 쓴답니다.
위 예제에서는 $button이 이벤트 타깃, onclick이 이벤트 타입, function이 이벤트 핸들러가 된다고 하네용
이 방식을 쓴면, HTML과 자바스크립트가 뒤섞이는 문제를 해결할 수 있지만, 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩할 수 있다는 단점이 있습니다.. 즉, 하나의 버튼에 함수 두개를 못 묶음!!
(3) addEventListener 메서드 방식
앞의 두 방식은 DOM Level 0부터 제공되던 오래된 방식이고, 이건 Level2부터 도입된 방식이라고 합니다.
EventTarget.addEventListener('eventType', functionName [, useCapture])
이런 방식으로 쓴다고 합니다. 여기서는 이벤트 타입에 on을 붙이지 않고 씁니다.
그래서 위의 예제를 이 방식으로 바꿔본다면,.,.
<button>Click me</button>
<script>
const $button = document.querySelector('button')
$button.addEventListener('click', function() {
console.log('hi')
})
</script>
얘는 위의 방식과 달리, 여러개를 달 수도 있어요
<button>Click me</button>
<script>
const $button = document.querySelector('button')
$button.addEventListener('click', function() {
console.log('one')
}
$button.addEventListener('click', function() {
console.log('two')
}
</script>
이때 호출 순서는 등록순 = one이 먼저 찍히고 two가 찍힘
근데 똑같은 함수 등록하면 한번만 등록됩니다
40.4 이벤트 핸들러 제거
addEventListener 방식으로 등록한 이벤트 핸들러는 EventTarget.prototype.removeEventListener 메서드를 통해 제거합니다. 이때, 인수는 addEventLister와 동일하게 사용해요
<button>Click me</button>
<script>
const $button = document.querySelector('button')
const func1 = console.log('one')
$button.addEventListener('click', fucn1)
$button.removeEventListener('click', func2) // 안됩니다
$button.removeEventListener('click', func1) // 이건 됩니다
</script>
그래서 무명함수로 등록하면 제거가 안됨..
<button>Click me</button>
<script>
const $button = document.querySelector('button')
$button.addEventListener('click', function() {
console.log('one')
}) // 이런건 제거가 안됩니다
</script>
그럼 addEventListener말고 다른 방식으로 등록한건요?
→ removeEventListener이걸로는 안되고, null을 할당해주세요
<button>Click me</button>
<script>
const $button = document.querySelector('button')
$button.onclick = function () {
console.log('Hi')
}
$button.onclick = null // 이렇게 지워주세요
</script>
40.5 이벤트 객체
이벤트가 발생하면 이벤트 객체가 동적으로 생성됨 → 이 이벤트 객체가 이벤트 핸들러의 첫번째 인수로 전달됨
<button id="myBtn">Click me</button>
<script>
const btn = document.getElementById('myBtn');
btn.addEventListener('click', function(e) {
console.log('이벤트 타입:', e.type); // 'click'
console.log('이벤트 대상:', e.target); // 버튼 요소
});
</script>
이런식으로 매개변수 e에 암묵적으로 할당이 된다. (만약 이벤트 핸들러 어트리뷰트 방식이라면 무조건 event로 써야함)
이벤트 객체 ?
→ 이벤트 객체도 생성자 함수에 의해 생성된 객체이므로, 공통 프로퍼티를 상속받음 + 이벤트 타입에 따라서 고유한 프로퍼티가 정의되어있음
btn.addEventListener('click', function(e) {
console.log(e.type); // 모든 이벤트 객체가 가진 공통 프로퍼티
console.log(e.target); // 모든 이벤트 객체가 가진 공통 프로퍼티
});
이런식으로 공통으로 가진 프로퍼티가 있고, 각 이벤트 마다 다르게 갖는 프로퍼티가 있음.
// 마우스 이벤트의 고유 프로퍼티
btn.addEventListener('click', function(e) {
console.log(e.clientX); // 마우스 이벤트에만 있는 프로퍼티
console.log(e.clientY); // 마우스 이벤트에만 있는 프로퍼티
});
// 키보드 이벤트의 고유 프로퍼티
input.addEventListener('keydown', function(e) {
console.log(e.keyCode); // 키보드 이벤트에만 있는 프로퍼티
console.log(e.key); // 키보드 이벤트에만 있는 프로퍼티
});
더 자세하게 각 이벤트마다 뭐가 있는지 알고 싶은 사람은 책 p.775-778을 읽어보세요
40.6 이벤트 전파
<ul>
<li>첫 번째 아이템</li>
<li>두 번째 아이템</li>
<li>세 번째 아이템</li>
</ul>
이런식으로 리스트가 있을 때, li중에 하나를 클릭하면 이벤트가 발생하고, 이때 생성되는 이벤트 객체는 이벤트를 발생시킨 두번째 li를 중심으로 DOM 트리를 통해 전파된다
이런 이벤트 전파는 방향에 따라 Capturing phase, Target phase, Bubbling phase로 나뉨
즉, 이벤트는 이벤트를 발생시킨 이벤트 타깃 뿐만 아니라, 상위 DOM요소에서도 캐치할 수 있음!
40.7 이벤트 위임
<ul>
<li>아이템 1</li>
<li>아이템 2</li>
<li>아이템 3</li>
</ul>
이런식으로 리스트가 있고, 각 리스트마다 클릭하면 어떤게 클릭되었는지 알고 싶어요
그랬을 때 각 li태그에 하나하나 이벤트를 등록하면 이런 문제가 생겨요
- li가 많을수록 이벤트 리스너도 그만큼 많이 생성되어 메모리가 낭비됩니다.
- 나중에 새로운 li가 추가되면 그 요소에도 별도로 이벤트를 등록해줘야 합니다.
그래서 쓰는게 이벤트 위임입니다. 부모인 ul에 이벤트를 하나만 등록하고, 이벤트 버블링을 활용해서 어떤 자식이 클릭되었는지 확인하는 방식입니다
<ul>
<li>아이템 1</li>
<li>아이템 2</li>
<li>아이템 3</li>
</ul>
<script>
document.querySelector('ul').addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
console.log('클릭된 아이템:', e.target.textContent);
}
});
</script>
이런식으로 쓰면
- 이벤트 리스너가 하나만 생성됩니다.
- 새로운 li가 추가되어도 자동으로 이벤트 처리가 가능합니다.
40.8 DOM 요소의 기본 동작 조작
**preventDefault**⇒ 이벤트 객체의 메서드로, DOM 요소의 기본 동작을 중단시킵니다. (예를 들어, a태그 클릭하면 href에 지정된 링크로 간다던가, checkbox 클릭하면 체크 or 해제되는 기능 등등)
구글로 이동
document.querySelector('a').addEventListener('click', function(e) {
e.preventDefault(); // a 태그의 기본 동작(페이지 이동) 중단
console.log('링크가 클릭됨!');
});
→ 구글로 이동하지 않음
**stopPropagation**⇒ 얘도 이벤트 객체의 메서드로, 이벤트 전파를 중단시킵니다.
<div>
<button>클릭</button>
</div>
<script>
const div = document.querySelector('div');
const button = document.querySelector('button');
div.addEventListener('click', () => {
console.log('div 클릭됨');
});
button.addEventListener('click', (e) => {
e.stopPropagation(); // 이벤트 버블링 중단
console.log('button 클릭됨');
});
</script>
→ 여기서는 div에 달린 이벤트는 발생하지 않음
40.9 이벤트 핸들러 내부의 this
(1) 이벤트 핸들러 어트리뷰트 방식
이 방식에서는 this = 전역 객체 window
(2) 이벤트 핸들러 프로퍼티 방식과 (3) addEventListener 메서드 방식
여기서는 this = 이벤트를 바인딩한 DOM 요소 = e.currentTarget과 같음
<button onclick="handleClick()">어트리뷰트 방식</button>
<button id="propertyBtn">프로퍼티 방식</button>
<button id="addEventBtn">addEventListener 방식</button>
<script>
// 1. 어트리뷰트 방식
function handleClick() {
console.log('어트리뷰트 방식의 this:', this);
// Window
// 2. 프로퍼티 방식
document.getElementById('propertyBtn').onclick = function(e) {
console.log('프로퍼티 방식의 this:', this);
// <button id="propertyBtn">프로퍼티 방식</button>
console.log('e.currentTarget:', e.currentTarget);
// <button id="propertyBtn">프로퍼티 방식</button>
};
// 3. addEventListener 방식
document.getElementById('addEventBtn').addEventListener('click', function(e) {
console.log('addEventListener 방식의 this:', this);
// <button id="addEventBtn">addEventListener 방식</button>
console.log('e.currentTarget:', e.currentTarget);
// <button id="addEventBtn">addEventListener 방식</button>
});
</script>
40.10 이벤트 핸들러에 인수 전달
아까 1번 방식은 함수에 인수 전달이 가능했었음. 근데 2번과 3번 방식은 어떻게 하나요?
<button id="propertyBtn">프로퍼티 방식</button>
<button id="addEventBtn">addEventListener 방식</button>
<script>
// 이벤트 핸들러
function handleClick(name) {
console.log(`${name}님이 클릭했습니다!`);
}
// 2. 프로퍼티 방식 - 인수 전달
document.getElementById('propertyBtn').onclick = () => handleClick('Kim');
// 3. addEventListener 방식 - 인수 전달
document.getElementById('addEventBtn').addEventListener('click', () => handleClick('Lee'));
</script>
40.11 커스텀 이벤트
= 개발자의 의도대로 생성된 이벤트
<button id="myBtn">클릭하세요</button>
<script>
// 1. 마우스 이벤트 기반의 커스텀 이벤트 생성
const customMouseEvent = new MouseEvent('customClick', {
bubbles: true, // 버블링 활성화
clientX: 100, // X 좌표 지정
clientY: 200, // Y 좌표 지정
view: window
});
const btn = document.getElementById('myBtn');
// 2. 커스텀 이벤트에 대한 이벤트 핸들러 등록 -> 마우스가 어디에 있던 항상 100,200이 나옴
btn.addEventListener('customClick', function (e) {
console.log('X 좌표:', e.clientX); // 100
console.log('Y 좌표:', e.clientY); // 200
});
// 3. 버튼 클릭 시 커스텀 이벤트 발생
btn.addEventListener('click', function() {
this.dispatchEvent(customMouseEvent);
});
</script>
생성한 커스텀 이벤트는 dispatchEvent를 통해 실행시킬 수 있음
스터디하면서 정리한 것들 아카이빙중..
'FrontEnd > JavaScript' 카테고리의 다른 글
[JS] Set과 Map (0) | 2024.12.22 |
---|---|
[JS] 정규표현식 (1) | 2024.12.21 |
[Javascript] 프로토타입(2) 프로토타입 객체 (1) | 2024.11.10 |
[Javascript] 프로토타입(1) 객체지향 프로그래밍과 상속 (0) | 2024.11.09 |
[Javascript] 일급 객체, 함수의 프로퍼티 (0) | 2024.10.27 |