1. 이벤트
이벤트 (event) : 웹 페이지에서 특정 동작이나 상호작용이 발생했을 때 실행되는 작업을 의미
브라우저는 처리해야할 특정 사건이 발생하면 이를 감지하여 이벤트를 발생시킨다.
예를들어
버튼을 클릭했을 때 함수를 호출하여 처리를 하고 싶다고 가정해보자!
이때 브라우저가 버튼 클릭을 감지하여 클릭 이벤트를 발생시킬 수 있다.
특정 버튼 요소에서 클릭 이벤트가 발생하면 특정 함수를 호출하도록 브라우저에게 위임할 수 있다.
<html>
<body>
<button>Click me!</button>
<script>
const $button = document.querySelector('button');
$button.onclick = () => { alert('button clik'); };
</script>
</body>
</html>
window.Document.HTMLElment 타입의 객체는 onclick 같이 특정 이벤트에 대응하는 다양한
이벤트 핸들러 프로퍼티를 가지고 있다.
해당 이벤트가 발생했을 때 할당한 함수가 브라우저에 의해 호출된다.
2. 이벤트타입
2-1. 마우스 이벤트
이벤트타입 | 이벤트 발생시점 |
click | 마우스 버튼 클릭 |
dbclick | 마우스 버튼 더블 클릭 |
mousedown | 마우스 버튼 누를 때 |
mouseup | 누르고 있던 마우스 버튼 놓았을 때 |
mousemove | 마우스 커서 움직 |
mouseenter | 마우스 커서를 HTML요소 안으로 이동(버블링x) |
mouseover | 마우스 커서를 HTML 요소 안으로 이동(버블링O) |
mouseleave | 마우스 커서를 HTML 요소 밖으로 이동(버블링x) |
mouseout | 마우스 커서를 HTML 요소 밖으로 이동(버블링O) |
2-2. 키보드 이벤트
이벤트타입 | 이벤트 발생시점 |
keydown | 모든 키 눌렀을 때 |
keypress | 문자 키를 눌렀을 때 연속적으로 발생 -> 폐지 됨 |
keyup | 누르고 있던 키를 놓았을 때 한번만 발생 |
2-3. .포커스 이벤트
이벤트타입 | 이벤트 발생시점 |
focus | HTML 요소가 포커스를 받았을 때 (버블링x) |
blur | HTML 요소가 포커스를 잃었을 때 (버블링x) |
focusin | HTML 요소가 포커스 받았을 때 (버블링o) |
focusout | HTML 요소가 포커스 잃었을 때 (버블링o) |
2-4. 뷰 이벤트
이벤트타입 | 이벤트 발생시점 |
resize | 브라우저 윈도우 (window) 크기를 리사이즈할 때 연속적으로 발생 |
scroll | 웹페이지 또는 HTML 요소 스크롤 할 때 연속적 발생 |
잠시만! 버블링...이 무엇일까?
버블링(bubbling)은 이벤트 전파(Event Propagation) 과정 중 하나로,
이벤트가 발생한 요소에서 시작해 상위 요소로 전파되는 동작을 의미.
즉, 특정 요소에서 이벤트가 발생하면, 해당 이벤트는 그 요소에서 처리된 후 부모 요소로 전달되고, 계속해서 상위 요소들로 전달되는 방식..
즉 이벤트는 가장 안쪽의 요소에서 먼저 발생한다. .
그리고 이벤트가 발생한 요소에서 시작해, 그 요소의 부모, 부모의 부모 등 DOM 트리의 루트까지 상위 요소로 전파되는 것이
버블링의 과정..
3. 이벤트 핸들러 등록
이벤트 핸들러 : 이벤트가 발생했을 때 브라우저에 호출을 위임한 함수 // 즉 이벤트가 발생하면 브라우저에 의해 호출될 함수
이벤트 핸들러 등록 : 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것
브라우저가 이벤트 핸들러를 호출하기 위해서는 콜백 함수처럼 함수 참조를 등록해야 호출할 수 있다.
3-1. 이벤트 핸들러 어트리뷰트 방식
onclick 같이 on 접두사와 함께 이벤트의 종류를 나타내는 이벤트 타입으로 이루어짐
이벤트 핸들러 어트리뷰트 값으로 함수 호출문 등의 문을 할당하면 이벤트 핸들러가 등록된다.
<html>
<body>
<button onClikck = "sayHi('Lee')"> Click me !</button>
<script>
function sayHi(name) {
console.log(`안녕하세요 ${name}.`};
}
</script>
</body>
</html>
주의할점 !
이벤트 핸들러 에 이벤트 핸들러에 함수 참조 대신 함수 호출문을 직접 등록하면 예상치 못한 동작이 발생할 수 있다
왜냐하면 함수 호출문의 평가 결과가 이벤트 핸들러로 등록되기 때문에 문제가 발생할 수 있다.
즉 HTML 속성으로 이벤트 핸들러를 지정할 때는 함수 호출문을 작성해야 이벤트 발생시에 실행이 된다.
JS 코드로 이벤트 핸들러를 등록할 때는 함수 참조를 전달해야한다.
함수 호출문을 JS 이벤트 핸들러에 등록하면 즉시 실행되기 때문이다.
만약 함수 참조가 아니라 함수 호출문을 등록하면 함수 호출문의 평가 결과가 이벤트 핸들러로 드록된다.
함수를 반환하는 고차 함수 호출문을 이벤트 핸들러로 등록한다면 아무런 문제가 없겠지만
함수가 아닌 값을 반환하는 함수 호출문을 이벤트 핸들러로 등록하면 브라우저가 이벤트 핸들러를 호출할 수 없다.
함수 참조 란 함수의 이름을 그대로 전달하여 브라우저가 나중에 이벤트 발생 시 함수를 호출 하는 것이다.
함수 호출문이란 괄호를 붙여서 함수를 즉시 실행하고, 그 실행 결과(반환값)를 이벤트 핸들러로 등록하는 것이다.
중요한 핵심은 함수 호출문의 평가 결과가 이벤트 핸들러로 등록되기 때문에 문제가 생길 수 있다
따라서 이벤트 핸들러에 함수 참조 대신 함수 호출문을 직접 등록하면 예상치 못한 동작이 발생할 수 있다
function myFunction() {
console.log('버튼이 클릭되었습니다!');
}
// 함수 참조 전달
document.getElementById('myButton').addEventListener('click', myFunction);
이렇게 함수 참조를 전달하면 올바르게 전달이 되지만
function myFunction() {
console.log('버튼이 클릭되었습니다!');
}
// 함수 호출 전달
document.getElementById('myButton').addEventListener('click', myFunction());
myFunction()은 괄호를 사용해 즉시 실행된다.
이벤트가 발생하기 전에 함수가 실행되고, 이후 이벤트 발생 시에는 아무 동작도 안 한다.
"함수 호출문"을 이벤트 핸들러로 쓰면 실제로 이벤트가 발생했을 때 실행할 함수가 없게 된다.
특히 여기서 MyFunction() 이 호출되면서 반환된값이 onclick 이 할당이 되면서
실제로 이벤트가 발생했을 때 실행할 함수가 없게된다.
document.getElementById('myButton').onclick = myFunction(); // 잘못된 방식
3-2. 이벤트 핸들러 프로퍼티 방식
window 객체와 Document.HTMLElement 타입의 노드객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있다.
onClick 같이 on 접두사와 이벤트의 종류를 나타내는 이벤트 타입으로 이루어져 있다.
함수를 바인딩하면 이벤트 핸들러가 등록된다.
<button>Click me</button>
<script>
const $button = document.querySelector('button');
$button.onclick = function () {
console.log('button click');
};
</script>
이벤트 핸들러를 등록하기 위해서는 이벤트를 발생시킬 객체인 이벤트타깃과 이벤트의 종류를 나타내는 문자열인
이벤트 타입 그리고 이벤트 핸들러를 지정할 필요가 있다. .
버튼 요소 = 이벤트 타깃
click = 이벤트 타입
handleClick = 이벤트 핸들러
3-3. addEventListener 메서드 방식
addEventListener 메서드의 첫 번째 매개변수에는 이벤트의 종류를 나타내는 문자열인 이벤트 타입을 전달한다.
이때 이벤트 핸들러 프로퍼티 방식과 달리 on 접두사 붙이지 않음
addEventListenr 메서드는 이벤트 핸들러를 인수로 전달한다.
동일한 HTML 요소에서 발생한 동일한 이벤트에 대해 이벤트 핸들러 프로퍼티 방식은
하나 이상의 이벤트 핸들러를 등록할 수 없지만 addEventListener 메서드는 하나 이상의 이벤트 핸들러를 등록할 수 있다.
그런데 참조가 동일한 이벤트 핸들러를 중복 등록하면
하나의 이벤트 핸들러만 등록됨 ! !
4. 이벤트 핸들러 제거
이벤트 핸들러를 제거하려면 EventTarget.prototype.removeEventListener 메서드를 사용한다.
단 addEventListeer 메서드와 removeEventListener 메서드에 전달한 인수가 일치하지 않으면
이벤트 핸들러가 제거되지 않음...
이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러를 제거하려면 이벤트 핸들러 프로퍼티에 null 을 할당한다.
$button.onClick = handleClick;
// 이벤트 핸들러 프로퍼티 방식으로 이벤트 핸들러 등록
$button.removeEventListener ('click', handleClick);
//removeEventListener 메서드로 이벤트 핸들러를 제거할 수 없음
5. 이벤트 객체
이벤트가 발생하면 이벤트에 관련한 다양한 정보를 담고 있는 이벤트 객체가 동적으로 생성
-생성된 이벤트 객체는 이벤트 핸들러의 첫번째 인수로 전달
클릭 이벤트가 발생하면 브라우저가 자동으로 이벤트 객체를 생성하여 이벤트 핸들러에 전달한다.
즉, 이벤트 핸들러 함수는 클릭 이벤트에 대한 정보를 담은 이벤트 객체(event object)를 첫 번째 매개변수로 전달받는다는 이야기야.
즉 매개변수 없이 작성하면 클릭이벤트 자체는 발생하지만 이벤트 객체의 정보를 사용할 수 없다.
const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log('버튼이 클릭되었습니다!');
});
e 는 브라우저가 전달해준 이벤트 객체를 전달받는 매개변수이다.
클릭이벤트에 대한 정보를 출력할 수 있다.
const button = document.querySelector('button');
button.addEventListener('click', (e) => {
console.log(e); // 이벤트 객체가 출력됨
});
또한 이벤트 핸들러 어트리뷰트의 방식경우 이벤트 객체를 전달받으려면 첫번째 매개변수 이름이 반드시!! event 여야 한다.
만약 event 가 아닌 다른 이름으로 매개변수 선언하면 전달받지 못함 . . .
6. 이벤트 전파
DOM 트리상에 존재하는 DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파된다.
이걸 이벤트 전파라고 하는데 이벤트 타깃을 중심으로 DOM 트리를 통해 전파된다.
캡쳐링 단계 : 이벤트가 상위 요소에서 하위 요소 방향으로 전파
타깃단계 : 이벤트가 이벤트 타깃에 도달
버블링단계 : 이벤트가 하위 요소에서 상위요소로 전파
6. 이벤트 위임
부모 요소에 이벤트 핸들러를 등록해서 자식 요소의 이벤트를 처리하는 방법
즉 여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 방법
-> 이벤트 타깃은 물론 상위 DOM 요소에서도 캐치할 수 있다.
이벤트 위임을 통해 상위 DOM 요소에 이벤트 핸들러를 등록하면 여러 개의 하위 DOM 요소에 이벤트 핸들러를 등록할 필요가 없다.
또한 동적으로 하위 DOM 요소를 추가하더라도 일일이 추가된 DOM 요소에 이벤트 핸들러를 등록할 필요가 없다.
이 방법은 DOM 트리에서 이벤트 버블링을 활용해서 동적으로 추가된 요소에도 이벤트를 적용할 수 있게 해준다!
const parent = document.getElementById('parent');
const addButton = document.getElementById('addButton');
// 부모 요소에 이벤트 핸들러 등록
parent.addEventListener('click', (event) => {
if (event.target && event.target.classList.contains('child')) {
console.log(`${event.target.textContent} 클릭됨!`);
}
});
// 버튼 동적 추가
addButton.addEventListener('click', () => {
const newButton = document.createElement('button');
newButton.className = 'child';
newButton.textContent = `Button ${parent.children.length + 1}`;
parent.appendChild(newButton);
});
새 버튼을 동적으로 추가해도 parent의 이벤트 핸들러가 이를 처리.
이벤트 위임 덕분에 동적으로 생성된 요소에도 추가 코드를 작성하지 않아도 이벤트가 적용됨.
'Deep Dive 정리' 카테고리의 다른 글
[JS Deep Dive] 41장 - 타이머 (0) | 2024.12.26 |
---|---|
[JS Deep Dive] 39장 - DOM (3) (0) | 2024.11.21 |
[JS Deep Dive] 39장 - DOM (2) (0) | 2024.11.21 |
[JS Deep Dive] 39장 - DOM (0) | 2024.11.21 |
[JS Deep Dive] 35장 - 스프레드 문법 (0) | 2024.11.14 |