fffo
이벤트 본문
이벤트 드리븐 프로그래밍
- 이벤트 : 무언가 일어났다는 신호
- 이벤트 핸들러 : 이벤트가 발생했을 때 실행되는 함수
- 이벤트 핸들러 등록 : 브라우저에게 이벤트 핸들러의 호출을 위임
- 브라우저가 행동을 감지해 이벤트를 발생 시킴
- 이벤트 드리븐 프로그래밍 : 이벤트 중심으로 프로그램의 흐름을 제어하는 프로그래밍 방식
이벤트 타입
- 이벤트 타입 : 이벤트의 종류를 나타내는 문자열
이벤트 핸들러 등록
이벤트 핸들러 어트리뷰트 방식
- HTML 요소의 이벤트 핸들러 어트리뷰트에 값으로 문(statement)을 할당
- 각 어트리뷰트 이름은 'on + 이벤트 타입'
<button onclick="sayHi('dlwlrma')">Click me</button>
<script>
function sayHi(name) { console.log(`Hi! ${name}.`); }
</script>
- 이벤트 핸들러 어트리뷰트 값은 암묵적으로 생성될 이벤트 핸들러의 몸체를 의미함
- 위 예에서 onclick="sayHi('dlwlrma')" 어트리뷰트는 파싱되어 다음과 같은 함수를 암묵적으로 생성 함
function onclick(event) { sayHi('dlwlrma'); }
- 생성한 함수는 이벤트 핸들러 어트리뷰트 이름과 동일한 키인 onclick 이벤트 핸들러 프로퍼티에 할당 함
- 함수의 참조를 할당하는 것이 아닌 함수 호출문을 할당하는 이유는 이벤트 핸들러에 인수를 전달하기 위함
이벤트 핸들러 프로퍼티 방식
- window 객체, Document, HTMLElement 타입의 DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가짐
- 각 프로퍼티 키는 이벤트 핸들러 어트리뷰트의 이름과 같이 on + 이벤트타입
- 이벤트 핸들러 프로퍼티에 함수를 바인딩해서 이벤트 핸들러 등록
<button>Click me</button>
<script>
const $button = document.querySelector('button');
$button.onclick = function () { console.log('clicked'); };
</script>
- 결과적으로 이벤트 핸들러 어트리뷰트와 동일하지만 하나의 프로퍼티에 두 개 이상의 이벤트 핸들러 등록을 하지 못하는 단점이 있음
- 어트리뷰트에 두 번 이상 핸들러를 등록 시 핸들러가 덮어 씌워짐
addEventListener 메서드 방식
- DOM Level 2에서 도입된 EventTarget.prototype.addEventListener 메서드를 이용해 핸들러 등록
- 앞선 두 방식은 DOM Level 0부터 제공되던 방식
[EventTarget].addEventListener(['eventType'], [functionName] [, options]);
- 첫 번째 인자로 전달되는 이벤트 타입에는 접두사 on이 붙지 않음
- 마지막 인자는 다음의 프로퍼티를 갖는 객체
- once : true이면 이벤트가 트리거 될 때 리스너가 자동으로 삭제됨
- capture :true는 캡처링 당계에서 이벤트를 캐치, false는 버블링 단계에서 이벤트를 캐치
- passive : true면 핸들러가 preventDefault를 호출하지 않음
- option 객체가 아닌 false/true로 할당 시 capture의 값으로 지정됨
- addEventListener 메서드 방식은 프로퍼티에 바인딩된 이벤트 핸들러에 아무 영향을 주지 않음
- addEventListener 메서드 방식과 프로퍼티 바인딩 형식을 모두 사용해서 같은 이벤트에 핸들러 등록 시 두 핸들러 모두 작동함
- addEventListener 메서드는 하나 이상의 핸들러 등록 가능, 등록된 순서대로 호출됨
- 단, 참조가 동일한 핸들러를 중복 등록 시 하나만 등록 됨
<button>Click me</button>
<script>
const $button = document.querySelector('button');
$button.addEventListener('click', function () {
console.log('clicked');
});
</script>
이벤트 핸들러 제거
- addEventListener로 등록한 핸들러는 removeEventListener 메서드를 사용해 제거 가능
- removeEventListener에 전달한 인수가 addEventListener로 등록했던 인수와 동일하면 제거됨
- 인수가 동일해야 하기 때문에 무명 함수로 핸들러 등록 시 제거 할 수 없음
- 프로퍼티 방식으로 등록한 핸들러는 removeEventListener로 제거 불가
- 프로퍼티에 null을 할당해 제거
이벤트 객체
- 이벤트 발생 시 이벤트 정보를 담은 이벤트 객체가 동적으로 생성됨
- 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달됨
- 어트리뷰트 방식으로 이벤트 객체를 전달 받으려면 이벤트 핸들러의 첫 번째 매개변수 이름이 반드시 event여야 함
- 이벤트 객체도 생성자 함수에 의해 생성되어 프로토타입 체인의 일원이므로 상위 객체 프로토타입 프로퍼티를 상속받음
이벤트 전파
- DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파됨
- 캡처링 : 상위 → 하위 방향으로 전파
- 버블링 : 하위 → 상위 방향으로 전파
- 이벤트 전파로 상위 DOM요소에서 이벤트 캐치 가능
- 버블링을 통해 전파되지 않는 이벤트들이 있지만 대체할 수 있는 이벤트가 존재하므로 캡처링 단계에서 이벤트를 캐치해야 할 경우는 거의 없음
- 버블링 안 됨 / 버블링 됨(대체)mouseleave / mouseoutblur / focusout
- focus / focusin
- mouseenter / mouseover
이벤트 위임
- 상위 요소에 이벤트를 위임함으로써 이벤트 핸들러 등록을 효율적으로 관리 가능
<!DOCTYPE html>
<html>
<head>
<title>bbubbu</title>
<meta charset="UTF-8" />
<style>
.active {
color: red;
}
</style>
</head>
<body>
<ul id="fruits">
<li id="apple" class="active">Apple</li>
<li id="banana">Banana</li>
<li id="orange">Orange</li>
</ul>
<p>선택된 아이럼: <em class="msg">apple</em></p>
<script>
const $fruits = document.querySelector('#fruits');
const $msg = document.querySelector('.msg');
function activate({ target }) {
if (!target.matches('#fruits > li')) return;
[...$fruits.children].forEach($fruit => {
$fruit.classList.toggle('active', $fruit === target);
$msg.textContent = target.id;
});
}
$fruits.addEventListener('click', activate);
</script>
</body>
</html>
- 이벤트 객체의 currentTarget 프로퍼티는 핸들러가 바인딩된 DOM 요소를 가리키고 target 프로퍼티는 이벤트를 발생시킨 DOM요소를 가리킴
DOM요소의 기본 동작 조작
기본 동작 중단
- 이벤트 객체의 preventDefault 메서드는 DOM 요소의 기본 동작을 중단 시킴
이벤트 전파 방지
- stopPropagation
이벤트 핸들러 내부의 this
이벤트 핸들러 어트리뷰트 방식
- 어트리뷰트 방식은 암묵적으로 새로운 함수가 만들어지고 그 함수의 몸체에 핸들러의 호출문이 들어가는 형식이므로 this는 중첩함수, 즉 일반 함수로써의 호출이 되어 전역객체가 할당됨
- 단, 인수로 this를 전달하면 새로 만든 함수 몸체에서의 this가 되므로 이 함수의 this가 됨. 이는 프로퍼티 메서드로 할당되는 함수이므로 this는 이벤트를 바인딩한 DOM요소를 가리킴
이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식
- 둘 모두 this는 이벤트를 바인딩한 DOM요소를 가리킴
- 단, 화살표 함수로 정의한 이벤트 핸들러 내부의 this는 상위 스코프의 this를 가리킴에 유의
- 클래스에서 이벤트 핸들러를 바인딩할 때 this에 주의해야 함
<!DOCTYPE html>
<html>
<head>
<title>document</title>
<meta charset="UTF-8" />
</head>
<body>
<button class="btn">0</button>
<script>
class App {
constructor() {
this.$button = document.querySelector('.btn');
this.count = 0;
this.$button.onclick = this.increase.bind(this);
}
increase() {
this.$button.textContent = ++this.count;
}
}
new App();
</script>
</body>
</html>
- 클래스 필드에 할당한 화살표 함수를 핸들러로 등록해 핸들러 내부의 this가 인스턴스를 가리키도록 할 수 있지만 이 때 핸들러는 프로토타입 메서드가 아닌 인스턴스 메서드가 됨
이벤트 핸들러에 인수 전달
- 인수 전달은 함수 호출 시에 가능함
- 따라서 어트리뷰트 방식을 제외한 프로퍼티 방식, addEventListener 메서드 방식은 브라우저가 호출하기 때문에 인수를 전달할 수 없음
- 그러나 핸들러 내부에서 함수를 호출해 인수를 전달하거나 핸들러를 반환하는 함수를 호출해 인수를 전달 가능
<!DOCTYPE html>
<html>
<head>
<title>document</title>
<meta charset="UTF-8" />
</head>
<body>
<label>User name <input type="text"></label>
<em class="message"></em>
<script>
const MIN_USER_NAME_LENGTH = 5;
const $input = document.querySelector('input[type=text]');
const $msg = document.querySelector('.message');
const checkUserNameLength = min => {
$msg.textContent = $input.value.length < min ? `이름은 ${min}자 이상 입력해주세용` : '';
};
$input.onblur = () => {
checkUserNameLength(MIN_USER_NAME_LENGTH);
}
</script>
</body>
</html>
커스텀 이벤트
커스텀 이벤트 생성
- 이벤트 객체는 이벤트 생성자 함수로 생성 가능
- 생성자 함수로 명시적으로 생성한 이벤트 객체는 임의의 이벤트 타입을 지정 가능
'Programming > Javascript' 카테고리의 다른 글
REST API (0) | 2021.11.20 |
---|---|
Ajax (0) | 2021.11.20 |
자바스크립트 비동기 처리 (0) | 2021.11.10 |
자바스크립트에서의 this 키워드 (0) | 2021.11.08 |
브라우저 저장소들의 차이점 (로컬스토리지, 세션스토리지, 쿠키) (0) | 2021.11.05 |
Comments