생활코딩 - 웹브라우저 Javascript (자바스크립트)
위 강의를 듣고 정리한 글입니다.
이벤트(event)는 어떤 사건을 의미한다. 브라우저에서의 사건이란 사용자가 클릭을 했을 '때', 스크롤을 했을 '때', 필드의 내용을 바꾸었을 '때'와 같은 것을 의미한다.
<!DOCTYPE html>
<html>
<body>
<input type="button" onclick="alert(window.location)" value="alert(window.href)" />
<input type="button" onclick="window.open('bom.html')" value="window.open('bom.html')" />
</body>
</html>
onclick 속성의 자바스크립트 코드(alert(window.location))는 사용자가 이 버튼을 클릭 했을 '때' 실행된다.
즉 js 개발자는 어떤 일이 발생했을 때 실행 되어야 하는 코드를 등록하고, 브라우저는 그 일이 발생했을 때 등록된 코드를 실행하게 된다.
이러한 방식을 이벤트 프로그래밍이라고 한다.
event target
target은 이벤트가 일어날 객체를 의미한다.
<input type="button" onclick="alert(window.location)" value="alert(window.href)" />
event type
이벤트의 종류를 의미한다. 위의 예제에서는 click이 이벤트 타입이다. 그 외에도 scroll은 사용자가 스크롤을 움직였다는 이벤트이고, mousemove는 마우스가 움직였을 때 발생하는 이벤트이다.
이벤트의 종류는 이미 약속되어 있다.
아래 링크는 브라우저에서 지원하는 이벤트의 종류를 보여준다.
https://developer.mozilla.org/en-US/docs/Web/Reference/Events
event handler
이벤트가 발생했을 때 동작하는 코드를 의미한다. 위의 예제에서는 alert(window.location)이 여기에 해당한다.
이벤트 프로그래밍을 하기 위해서는 이벤트의 대상에 이벤트 핸들러를 등록해줘야 한다.
웹브라우저에서는 크게 3가지 종류의 등록방법을 제공한다
인라인 방식은 이벤트를 이벤트 대상의 태그 속성으로 지정하는 것이다.
<input type="button" onclick="alert('Hello world');" value="button" />
이벤트가 발생한 대상을 필요로하는 경우 this를 통해서 참조할 수 있다.
<!--자기 자신을 참조하는 불편한 방법-->
<input type="button" id="target" onclick="alert('Hello world, '+document.getElementById('target').value);" value="button" />
<!--this를 통해서 간편하게 참조할 수 있다-->
<input type="button" onclick="alert('Hello world, '+this.value);" value="button" />
인라인 방식의 장단점
장점: 태그에 이벤트가 포함되기 때문에 이벤트의 소재를 파악하는 것이 편리하다.
단점 : 정보인 HTML과 제어인 JavaScript가 혼재된 형태이기 때문에 바람직한 방법이 아니다.
프로퍼티 리스너 방식은 이벤트 대상에 해당하는 객체의 프로퍼티로 이벤트를 등록하는 방식이다.
인라인 방식에 비해서 HTML과 JavaScript를 분리할 수 있다는 점에서 선호되는 방식이지만 뒤에서 배울 addEventListener 방식을 추천한다.
<input type="button" id="target" value="button" />
<script>
var t = document.getElementById('target');
t.onclick = function(){
alert('Hello world');
}
</script>
이벤트 객체
이벤트가 실행된 맥락의 정보가 필요할 때는 이벤트 객체를 사용한다.
이벤트 객체는 이벤트가 실행될 때 이벤트 핸들러의 인자로 전달된다.
<body>
<input type="button" id="target" value="button" />
<script>
var t = document.getElementById('target');
t.onclick = function(event){
alert('Hello world, '+event.target.value)
}
</script>
ie8 이하 버전에서는 이벤트 객체를 핸들러의 인자가 아니라 전역객체의 event 프로퍼티로 제공한다.
또한 target 프로퍼티도 지원하지 않는다.
<input type="button" id="target" value="button" />
<script>
var t = document.getElementById('target');
t.onclick = function(event){
var event = event || window.event;
var target = event.target || event.srcElement;
alert('Hello world, '+target.value)
}
</script>
addEventListener는 이벤트를 등록하는 가장 권장되는 방식이다.
addEventListener는 여러개의 이벤트 핸들러를 등록할 수 있다.
<input type="button" id="target" value="button" />
<script>
var t = document.getElementById('target');
t.addEventListener('click', function(event){
alert('Hello world, '+event.target.value);
});
</script>
이 방식은 ie8이하에서는 호환되지 않는다.
ie에서는 attachEvent 메소드를 사용해야 한다.
var t = document.getElementById('target');
if(t.addEventListener){
t.addEventListener('click', function(event){
alert('Hello world, '+event.target.value);
});
} else if(t.attachEvent){
t.attachEvent('onclick', function(event){
alert('Hello world, '+event.target.value);
})
}
하나의 이벤트 대상에 복수의 동일 이벤트 타입 리스너를 등록할 수 있다.
<input type="button" id="target" value="button" />
<script>
var t = document.getElementById('target');
t.addEventListener('click', function(event){
alert(1);
});
t.addEventListener('click', function(event){
alert(2);
});
</script>
이벤트 객체를 이용하면 복수의 엘리먼트에 하나의 리스너를 등록해서 재사용할 수 있다.
<input type="button" id="target1" value="button1" />
<input type="button" id="target2" value="button2" />
<script>
var t1 = document.getElementById('target1');
var t2 = document.getElementById('target2');
function btn_listener(event){
switch(event.target.id){
case 'target1':
alert(1);
break;
case 'target2':
alert(2);
break;
}
}
t1.addEventListener('click', btn_listener);
t2.addEventListener('click', btn_listener);
</script>
HTML 태그는 중첩되어 있다.
따라서 특정한 태그에서 발생하는 이벤트는 중첩되어 있는 태그들 모두가 대상이 될 수 있다.
중첩된 태그들에 이벤트가 등록 되어 있다면 ?

<html>
<head>
<style>
html{border:5px solid red;padding:30px;}
body{border:5px solid green;padding:30px;}
fieldset{border:5px solid blue;padding:30px;}
input{border:5px solid black;padding:30px;}
</style>
</head>
<body>
<fieldset>
<legend>event propagation</legend>
<input type="button" id="target" value="target">
</fieldset>
<script>
function handler(event){
var phases = ['capturing', 'target', 'bubbling']
console.log(event.target.nodeName, this.nodeName, phases[event.eventPhase-1]);
}
document.getElementById('target').addEventListener('click', handler, true);
document.querySelector('fieldset').addEventListener('click', handler, true);
document.querySelector('body').addEventListener('click', handler, true);
document.querySelector('html').addEventListener('click', handler, true);
</script>
</body>
</html>
INPUT HTML capturing
INPUT BODY capturing
INPUT FIELDSET capturing
INPUT INPUT target
이벤트가 부모에서부터 발생해서 자식으로 전파되고 있다. 이러한 방식을 capturing이라고 한다. ie 낮은 버전에서는 작동하지 않기 때문에 많이 사용하지는 않는다.
document.getElementById('target').addEventListener('click', handler, false);
document.querySelector('fieldset').addEventListener('click', handler, false);
document.querySelector('body').addEventListener('click', handler, false);
document.querySelector('html').addEventListener('click', handler, false);
차이점은 addEventListener의 3번째 인자가 false로 변경 되었다.
실행결과
INPUT INPUT target
INPUT FIELDSET bubbling
INPUT BODY bubbling
INPUT HTML bubbling
이번에는 순서가 반대로 되었다. 자식부터 부모로 이벤트가 전파되는 것을 버블링(bubbling)이라고 한다.
이벤트 전파를 중간에 가로막을 수도 있다.
아래처럼 코드를 변경해보자.
function handler(event){
var phases = ['capturing', 'target', 'bubbling']
console.log(event.target.nodeName, this.nodeName, phases[event.eventPhase-1]);
}
function stophandler(event){
var phases = ['capturing', 'target', 'bubbling']
console.log(event.target.nodeName, this.nodeName, phases[event.eventPhase-1]);
event.stopPropagation();
}
document.getElementById('target').addEventListener('click', handler, false);
document.querySelector('fieldset').addEventListener('click', handler, false);
document.querySelector('body').addEventListener('click', stophandler, false);
document.querySelector('html').addEventListener('click', handler, false);
INPUT INPUT target
INPUT FIELDSET bubbling
INPUT BODY bubbling
참고로 ie9 이전의 브라우저에서는 이벤트 전파을 막기 위해서 event.cancelBubble 프로퍼티를 사용해야 한다.
웹브라우저의 구성요소들은 각각 기본적인 동작 방법을 가지고 있다.
텍스트 필드에 포커스를 준 상태에서 키보드를 입력하면 텍스트가 입력된다.
폼에서 submit 버튼을 누르면 데이터가 전송된다.
a 태그를 클릭하면 href 속성의 URL로 이동한다.
이러한 기본적인 동작들을 기본 이벤트라고 하는데 사용자가 만든 이벤트를 이용해서 이러한 기본 동작을 취소할 수 있다.
inline
이벤트의 리턴값이 false이면 기본 동작이 취소된다.
<p>
<label>prevent event on</label><input id="prevent" type="checkbox" name="eventprevent" value="on" />
</p>
<p>
<a href="http://opentutorials.org" onclick="if(document.getElementById('prevent').checked) return false;">opentutorials</a>
</p>
<p>
<form action="http://opentutorials.org" onsubmit="if(document.getElementById('prevent').checked) return false;">
<input type="submit" />
</form>
</p>
property 방식
리턴 값이 false이면 기본동작이 취소된다.
<p>
<label>prevent event on</label><input id="prevent" type="checkbox" name="eventprevent" value="on" />
</p>
<p>
<a href="http://opentutorials.org">opentutorials</a>
</p>
<p>
<form action="http://opentutorials.org">
<input type="submit" />
</form>
</p>
<script>
document.querySelector('a').onclick = function(event){
if(document.getElementById('prevent').checked)
return false;
};
document.querySelector('form').onclick = function(event){
if(document.getElementById('prevent').checked)
return false;
};
</script>
addEventListener 방식
이 방식에서는 이벤트 객체의 preventDefault 메소드를 실행하면 기본 동작이 취소된다.
<p>
<label>prevent event on</label><input id="prevent" type="checkbox" name="eventprevent" value="on" />
</p>
<p>
<a href="http://opentutorials.org">opentutorials</a>
</p>
<p>
<form action="http://opentutorials.org">
<input type="submit" />
</form>
</p>
<script>
document.querySelector('a').addEventListener('click', function(event){
if(document.getElementById('prevent').checked)
event.preventDefault();
});
document.querySelector('form').addEventListener('submit', function(event){
if(document.getElementById('prevent').checked)
event.preventDefault();
});
</script>