이벤트가 발생했을 때 실행되는 함수를 이벤트처리기 또는 이벤트 리스너라고 합니다.
등록방법
<input type='button' onclick='changeColor();'>
var btn = document.getElementById('button'); btn.onclick = changeColor();
var btn = document.getElementById('button'); btn.addEventListener('click', changeColor, false);
addEventListener를 이용하면 같은 이벤트 종류에 대해 여러개의 이벤트 리스너를 등록할 수 있음.
target.addEventListener(type, listener, useCapture);
target : 이벤트 리스너를 등록할 DOM노드
type : 이벤트 유형을 뜻하는 문자열('click', 'mouseup' 등)
listener : 이벤트가 발생했을 때 처리를 담당하는 콜백함수의 참조
useCapture : 이벤트 단계 ( true: 캡처링단계, false : 버블링단계(default)
<script>
window.onload = function() {
var element = document.getElementById('box');
element.addEventListener('click', function(e) {
e.currentTarget.style.backgroundColor = 'red';
}, false);
}
</script>
<!--... -->
<body>
<div id='box'>click me</div>
</body>
이벤트 리스너 함수가 받는 인수 e는 이벤트객체이며, 이벤트객체 안에는 발생한 이벤트의 정보가 담겨있습니다.
e.currentTarget은 클릭한 요소를 참조하는 요소객체입니다.
type에는 on이 빠진 이벤트타입이 대입됩니다.
listener부분에는 익명함수외에도 선언한 함수를 넣어줄 수 있습니다.
<script>
window.onload = function() {
var element = document.getElementById('box');
element.addEventListener('click', changeColor, false);
}
function changeColor(e) {
e.currentTarget.style.backgroundColor = 'red';
}
</script>
<body>
<div id='box'>click me</div>
</body>
changeColor함수를 선언한 후 해당 함수를 addEventListener를 통해 대입해줌.
target.removeEventListener(type, listener, useCapture);
주의
만약 addEventListener에 listener를 익명함수로 등록했다면
해당 이벤트리스너는 removeEventListener로 삭제할 수 없습니다
삭제하려면 그 함수의 참조를 변수나 배열에 저장하고 있어야합니다.
이벤트 리스너 안에서 removeEventListener를 호출하여 이벤트 리스너가 스스로 삭제하게 하기 위해선
listener에 arguments.callee를 넘깁니다.
(익명함수 내에서 removeEventListener를 호출하는 callee는 익명함수 그 자체이기 때문)
window.onload = function() {
var element = document.getElementById('box');
element.addEventListener('click', function() {
console.log('hello');
// removeEventListener 함수가 호출되는 순간에서의 호출자(callee)는 익명함수이므로
element.removeEventListener('click', arguments.callee, false);
}, false);
}
이벤트처리기와 이벤트리스너는 이벤트객체를 인수로 받습니다. (function(e) {...})
이벤트객체는 해당 이벤트의 다양한 정보를 저장한 프로퍼티와 이벤트의 흐름을 제어하는 메서드를 가지고 있습니다.
// 이벤트 객체 e
e.currentTarget.style.backgroundColor = 'red';
이벤트객체가 가지는 프로퍼티는 발생한 이벤트의 유형에 따라 달라집니다.
프로퍼티 | 값의 타입 | 설명 |
---|---|---|
type | 문자열 | 이벤트 이름('click', 'mousedown', 'keydown' 등) |
target | 요소 객체 | 이벤트가 발생한 요소 |
currentTarget | 요소 객체 | 처리를 담당하는 이벤트 리스너가 등록된 요소 객체 |
eventPhase | 정수 | 이벤트 전파 단계(1: 캡처링, 2: 타겟, 3: 버블링 단계) |
timeStamp | 정수 | 이벤트 발생시각(1970/01/01/00:00:00부터 경과된 밀리초) |
bubbles | 논리값 | 버블링 단계인지를 뜻하는 값 |
cancelable | 논리값 | preventDefault()로 기본이벤트를 취소할 수 있는지를 뜻하는 값 |
defaultPrevented | 논리값 | preventDefault()로 기본작업이 취소되었는지를 듯하는 값 |
isTrusted | 논리값 | 해당 이벤트가 사용자의 액션에 의해 생성되었는지를 뜻하는 값 |
click, dblclick, mousedown, mouseup, mousemove, mouseout, mouseover 등이 있습니다.
프로퍼티 | 값의 타입 | 설명 |
---|---|---|
screenX, screenY | 정수 | 클릭한 위치의 화면 좌표(컴퓨터 화면 왼쪽 위 기준) |
clientX, clienY | 정수 | 클릭한 위치의 윈도우 좌표(표시영역의 왼쪽 위 기준, 브라우저의 각 탭) |
pageX, pageY | 정수 | 클릭한 위치의 문서 좌표(문서 왼쪽 위 기준) |
offsetX, offsetY | 정수 | 이벤트가 발생한 요소의 상대좌표(요소의 왼쪽 위 꼭지점 기준) |
altKey | 논리값 | alt버튼이 눌렸는지 뜻하는 논리값 |
ctrlKey | 논리값 | ctrl이 눌렸는지 뜻하는 논리값 |
shiftKey | 논리값 | shift가 눌렸는지 뜻하는 논리값 |
detail | 정수 | 이벤트의 자세한 정보 : 마우스 이벤트의 경우 클릭한 횟수 |
button | 정수 | 눈린 마우스의 버튼(왼쪽 : 0, 휠 : 1, 오른쪽 : 2) |
relatedTarget | 객체 | mouseover 이베트에서는 마우스가 떠난 노드 |
mouseout 이벤트에서는 마우스가 들어온 노드 |
[##Image|kage@crWfXq/btq54OAmyEa/vD5ronWUyR7yjr1meCu6Lk/img.jpg|alignCenter|width="100%" data-origin-width="1439" data-origin-height="1251" data-ke-mobilestyle="widthOrigin"|||##]
window.onload = function() {
// 보더 박스를 생성한다.
var box = elt('div', null, 'JavaScript');
document.body.appendChild(box);
// 마우스를 press한 상대 위치를 저장하는 변수
var boxOffsetX, boxOffsetY;
// 보더박스의 설정
box.style.display = 'inline-block';
box.style.position = 'absolute';
box.style.padding = '10px';
box.style.backgroundColor = 'blue';
box.style.color = 'white';
box.style.cursor = 'pointer';
// mousedown 이벤트 리스너 등록
box.addEventListener('mousedown', function mouseDownListener(e) {
// mousemove 이벤트 리스너를 등록한다.
document.addEventListener('mousemove', mouseMoveListener, false);
// 마우스를 press한 상대 위치를 저장
boxOffsetX = e.offsetX;
boxOffsetY = e.offsetY;
}, false);
// mouseup 이벤트 리스너 등록
document.addEventListener('mouseup', function mouseUpListener(e) {
// mousemove 이벤트 리스너를 제거한다.
document.removeEventListener('mousemove', mouseMoveListener, false);
}, false);
// mousemove 이벤트 리스너
function mouseMoveListener(e) {
// 마우스로 press한 위치를 드래그한다.
box.style.left = e.pageX - boxOffsetX + 'px';
box.style.top = e.pageY - boxOffsetY + 'px';
}
}
클릭(mousedown) 시 mousemove이벤트가 등록되어 움직이면 박스위치가 변경됩니다.
mouseup시 박스가 움직이지 않도록 mousemove이벤트를 제거해줍니다.
프로퍼티 | 값의 타입 | 설명 |
---|---|---|
altKey | 논리값 | alt가 눌렸는지 뜻하는 논리값 |
ctrlKey | 논리값 | ctrl이 눌렸는지 뜻하는 논리값 |
shiftKey | 논리값 | shift가 눌렸는지 뜻하는 논리값 |
metaKey | 논리값 | Meta키가 눌렸는지 뜻하는 논리값(맥 : command, 윈도우 : 윈도우버튼) |
key | 문자열 | 눌린 키의 DOMString |
keyCode | 정수 | 눌린 키의 코드 |
code | 문자열 | 눌린 키가 키보드에서 차지하는 물리적 위치를 뜻하는 문자열 |
keyCode의 경우 알파벳, 숫자일때만 올바르게 표시됩니다.
또한 keyCode값은 shift, ctrl을 눌러도 바뀌지 않습니다.
Window객체나 XMLHttpRequest객체처럼 단독으로 존재하는 객채에서 이벤트가 발생하면
웹브라우저는 해당 객체에 등록된 이벤트 처리기를 호출합니다. 그러나 이벤트가 HTML요소에서 발생하면 그 요소는 물론 그 요소의 모든 조상요소에 이벤트를 전파합니다. 결과적으로 그 모든 요소에 등록된 이벤트 처리기가 호출됩니다.
HTML 요소에서 클릭 같은 사용자 행위와 프로그램 코드가 이벤트를 발생시킵니다.
이벤트가 발생한 요소는 이벤트 타겟이라고 합니다.
또한 이벤트를 발생시키는 것을 가리켜 이벤트를 발사(fire)한다고 표현합니다.
요소에서 이벤트가 발생하면 DOM트리의 관련요소(조상요소) 전체에
그 이벤트에 반응하는 이벤트 처리기나 이벤트 리스너가 등록되어있는지 확인하는 작업을 합니다.
그리고 등록된 함수가 있을때는 그 함수를 실행하도록 되어있습니다.
이벤트 전파
window.onload = () => {
var outer = document.getElementById('outer');
var inner2 = document.getElementById('inner2');
outer.addEventListener('click', function(e) {
console.log('outer bubbling');
}, false);
outer.addEventListener('click', function(e) {
console.log('outer capturing');
},true);
inner2.addEventListener('click', function(e) {
console.log('inner2 bubbling');
},false);
}
inner2 클릭 시 아래와 같이 콘솔이 나타납니다.
자식요소에서 발생한 이벤트는 부모 요소에서도 전파됩니다.
때문에 의도치 않은 동작을 할 경우가 있는데, 이벤트 전파를 취소해서 부모요소가 이벤트처리를 하지 않도록 만들 수 있습니다.
또한 이벤트가 발생했을때 웹브라우저의 기본 동작도 취소할 수 있습니다.
event.stopPropagation();
이벤트 리스너 안에서 이벤트 객체의 stopPropagation메서드를 호출하면 이벤트가 그 다음 요소로 전달되는것을 막습니다.
window.onload = () => {
var outer = document.getElementById('outer');
var inner2 = document.getElementById('inner2');
outer.addEventListener('click', function(e) {
console.log('outer bubbling');
}, false);
outer.addEventListener('click', function(e) {
console.log('outer capturing');
}, true);
inner2.addEventListener('click', function(e) {
console.log('inner2 (1)');
e.stopPropagation(); // 이벤트 전파를 취소한다.
}, false);
inner2.addEventListener('click', function(e) {
console.log('inner2 (2)');
}, false);
}
stopPropagation메서드를 통해 버블링 단계에서 이벤트를 전파하지 않습니다.
event.stopImmediatePropagation();
해당 객체에 등록된 다른 이벤트 리스너도 일시적으로 멈춥니다.
window.onload = () => {
var outer = document.getElementById('outer');
var inner2 = document.getElementById('inner2');
outer.addEventListener('click', function(e) {
console.log('outer bubbling');
}, false);
outer.addEventListener('click', function(e) {
console.log('outer capturing');
}, true);
inner2.addEventListener('click', function(e) {
console.log('inner2 (1)');
e.stopImmediatePropagation(); // 이벤트 전파를 일시정지한다
}, false);
inner2.addEventListener('click', function(e) {
console.log('inner2 (2)');
}, false);
}
event.preventDefault();
웹브라우저에 구현된 기본동작을 취소할 수 있습니다.
window.onload = () => {
var anchor = document.getElementById('school');
anchor.addEventListener('click', function(e) {
if (!confirm('페이지를 이동하시겠습니까?')) {
e.preventDefault();
}
}, false);
}
확인 버튼을 누르면 이동, 취소를 누르면 브라우저에 구현된 a태그의 기본동작이 되지 않습니다.
다만 이벤트 객체의 cancelable이 true일경우만 preventDefault로 취소가능합니다.
이벤트 리스너 안의 this값은 함수를 호출할 때 그 함수가 속해있던 객체의 참조입니다.
window.onload = () => {
function Person(name) {
this.name = name;
};
Person.prototype.sayHello = function() {
console.log(this);
console.log('Hello' + this.name);
}
var person = new Person('Tom');
var button = document.querySelector('#button');
button.addEventListener('click', person.sayHello, false);
}
버튼을 클릭하면 person.sayHello가 실행되고, 그 안의 this가 person을 가리킬거라 기대하지만 실제로는 button요소객체를 가리킵니다.
button객체의 name프로퍼티값은 빈문자열이므로 의도와는 다르게 표시됩니다.
window.onload = function() {
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log('hello' + this.name);
};
var person = new Person('Tom');
var button = document.querySelector('#button');
button.addEventListener(
'click',
person.sayHello.bind(person),
false
);
};
이벤트 처리기 또는 이벤트 리스너로 익명 함수를 넘기고, 익명 함수 안에서 메서드를 호출하면 그 메서드의 this가 메서드를 참조하는 객체를 가리킵니다.
button.addEventListener('click', function(e) {
person.sayHello();
}, false);
화살표 함수에서의 this는 함수를 초기화하는 시점에 결정됩니다.
이를 활용하여 객체 안의 메서드를 화살표 함수로 표기하면 그 안의 this가 생성된 객체를 가리킵니다.
window.onload = () => {
function Person(name) {
this.name = name;
this.sayHello = () => {
console.log('hello ' + this.name);
}
};
const person = new Person('Tom');
const button = document.querySelector('#button');
button.addEventListener('click', person.sayHello, false);
}
단, 화살표 함수 표현식으로 정의한 함수를 객체의 prototype의 메서드로 추가하면 의도한대로 동작하지 않습니다.
// prototype에 화살표 함수로 this를 사용하는 경우
// Window객체를 가리키게됨
Person.prototype.sayHello = () => {
console.log('hello ' + this.name);
}
addEventListener의 두번째 인수는 함수이지만 대신 객체를 넘길수도 있습니다. 그리고 그 객체에 handleEvent 메서드가 있으면 그 메서드를 이벤트 리스너로 등록하도록 만들어져있습니다.
function Person(name) {
this.name = name;
}
Person.prototype.handleEvent = function() {
console.log('hello ' + this.name);
}
var person = new Person('Tom');
var button = document.getElementById('button');
button.addEventListener('click', person, false);