목차
- 이벤트
- 이벤트 핸들러
2-1. HTML 속성 핸들러 1개 할당 가능
2-2. DOM 프로퍼티 핸들러 1개 할당 가능
2-3. addEventListener 표준 이벤트 모델, 핸들러 n개 할당 가능 - 이벤트 객체
- 버블링과 캡쳐링 이벤트 전달/차단
4-1. 버블링
4-2. 버블링 차단 event.stopPropagation()
4-3. 캡쳐링 true - 기본 이벤트 제거
5-1. event.preventDefault()
5-2. return false;
1. 이벤트
- DOM 이벤트(Document Object Model event)는 마우스 이벤트(click, mouseover, mouseout, mousemove), 폼 요소 이벤트(submit, focus), 키보드 이벤트(keydown, keyup), 문서 이벤트 등 그 종류가 다양하다.
- 또, 이러한 이벤트 속성과 연동하여 이벤트 발생 시 특정 기능을 실행시킬 함수를 이벤트 핸들러(event handler)라고 한다.
- 핸들러 할당 방법은 크게 세 가지가 있다: ① HTML 속성, ② DOM 프로퍼티, ③ addEventListener(표준 이벤트 모델)
2. 이벤트 핸들러
2-1. HTML 속성 핸들러 1개 할당 가능
<tag onclick=‘처리로직’></tag>
<tag onclick=‘스크립트 내 함수 호출’></tag>
<body>
<button onclick="alert('클릭하셨군요');">클릭해 보세요</button>
<button onmouseover="mouseover(this);">마우스를 올려보세요</button>
<script>
function mouseover(elem) {
alert('마우스 올리지 마세요!');
console.log(elem);
console.log(elem.innerHTML);
elem.innerHTML = '이곳에 마우스를 올릴 수 없습니다';
}
</script>
</body>
- HTML 내부 onevent 속성에 이벤트 핸들러를 할당하는 방법이다.
- 처리 로직 형태로 간단한 코드를 직접 작성하기도 하지만, 코드가 길다면 별도의 함수를 만들어 호출할 수도 있다.
- 핸들러 내부에 쓰인 this는 핸들러가 할당된 요소를 뜻한다. 즉 위 예시에서는 이벤트가 발생한 button을 가리킨다: mouseover(this);
2-2. DOM 프로퍼티 핸들러 1개 할당 가능
element.onclick = 이벤트핸들러(함수);
<body>
<button id="testA">testA 실행 확인</button>
<button id="testB">testB 실행 확인</button>
<div id="area" class="area"></div>
<script>
let testA = document.getElementById('testA');
let testB = document.getElementById('testB');
testA.onclick = function(){
document.getElementById('area').innerHTML
+= 'testA가 실행되었습니다.<br>';
console.log(this);
console.log(this.innerHTML);
};
testA.onclick = () => alert('이벤트 덮어쓰기!');
function removeEvent() {
document.getElementById('area').innerHTML
+= "testB가 실행되면서 testA 이벤트 제거<br>";
testA.onclick = null;
}
testB.onclick = removeEvent;
</script>
</body>
- 요소 객체가 가지고 있는 onevent 속성에 이벤트 핸들러를 연결한다.
- 단, 해당 프로퍼티는 하나만 존재하기 때문에
여러 개의 이벤트를 할당할 수는 없다.
- DOM 프로퍼티 통해 할당된 이벤트 핸들러 예시이다: testA.onclick = function(){...};
- innerHTML 통해 해당 문구를 += 하겠다고 작성하였으므로 매 클릭마다 문구가 늘어나 쌓인다.
- 한편, 콘솔창에 출력하기 위해 작성된 구문에서 this는 역시 핸들러 내부에서 핸들러가 할당된 요소를 가리키고 있다. 여기서는 testA를 말한다.
- testA.onclick에 대한 이벤트 핸들러 작성이 마쳐진 상태에서 또 한 번 쓰인다면, 즉 똑같은 이벤트 속성을 다시 작성한다면, 이전에 정의된 이벤트 핸들러는 무시되고 새롭게 작성한 내용이 덮어쓰기 처리된다. 프로퍼티는 하나의 이벤트밖에 연결할 수 없기 때문이다.
- 이벤트 객체를 제거할 때는 속성값으로 null을 대입한다: testA.onclick = null;
2-3. addEventListener 표준 이벤트 모델, 핸들러 n개 할당 가능
element.addEventListener(event, handler, [options])
element.removeEventListener(event, handler, [options])
addEventListener(이벤트명, 핸들러, 확장) | ※ 확장 : 버블링/캡쳐링 |
removeEventListener(이벤트명, 핸들러) | 이벤트 삭제 |
<body>
<button id="btn">실행확인</button>
<script>
let btn = document.querySelector("#btn");
btn.addEventListener('click', () => alert('첫 번째 이벤트 동작!'));
btn.addEventListener('click', () => btn.style.background = 'red');
function myFunction() {
alert('곧 삭제됩니다!');
}
btn.addEventListener('mouseover', myFunction);
btn.removeEventListener('mouseover', myFunction);
</script>
</body>
- W3에서 공식적으로 지정한 표준 이벤트 모델이다.
- 앞서 다룬 HTML 속성, DOM 프로퍼티 통한 할당 방법과 달리 한 번에 여러 이벤트 핸들러 설정이 가능하다.
- this 키워드로 이벤트 발생 객체를 가리킬 수 있다.
- 예시에서 역시 변수명.addEventListener()가 문제 없이 여러 번 쓰인 모습을 보인다. 이때 두 구문은 연달아 실행되며, 작성 순서에 따라 alert()가 실행되고 확인을 누르면 → style 속성이 발효돼 버튼 색상이 빨갛게 바뀐다.
- 핸들러를 삭제하기 위해서는 핸들러 할당 시 사용한 함수를 그대로 전달해야 한다.
하지만 상단의 예시에서는 함수를 호출하지 않고 간단히 선언하였으므로 삭제 테스트용 함수 myFunction()을 선언한다. - 핸들러 할당 시에 addEventListener()로 전달했던 인자(함수) 그대로 removeEventListener()에 돌려보낸다.
3. 이벤트 객체
❗ HTML 속성 안에서도 이벤트 객체 사용 가능하다. 이때는 다른 이름으로 쓰일 수 없으며, 인자를 반드시 event라고 명명해야 한다: console.log(event);
<body>
<button id="evtObj">이벤트 객체 확인</button>
<button onclick="console.log(event);">이벤트 객체 확인</button>
<script>
let evtObj = document.querySelector("#evtObj");
evtObj.onclick = function(evnet){
console.log(event);
console.log(event.type + "이벤트");
console.log(event.currentTarget);
console.log("에서 발생");
console.log("이벤트가 발생한 곳의 좌표는");
console.log(event.clientX + " : " + event.clientY);
};
</script>
</body>
- 이벤트 객체는 눌린 키보드 키, 마우스 클릭된 좌표 등 이벤트에 대한 상세한 정보를 가지고 있다.
- 이벤트가 발생하면 → 브라우저는 이벤트 객체를 만들어 → 핸들러에 인수 형태로 전달한다.
- 핸들러 내부에서 이벤트 타입과 요소 이벤트가 발생한 좌표 등을 확인할 수 있다.
<button onclick="console.log(event);">
- HTML 속성 안에서 쓰인 이벤트 객체 예시이다. 반드시 event라고 쓰여야 한다.
evtObj.onclick = function(event){...}
- DOM 프로퍼티 통해 이벤트 핸들러가 할당된 모습이다. 여기서 파라미터 이름은 event든 evt든 지정하기 나름이다.
- 작성된 스크립트에 의해 콘솔창에 출력된 결과이다.
- event는 PointerEvent 객체 형태로 값을 반환하고 있다.
- 그밖에도 타입(type), 현재 타깃(currentTarget), 브라우저상에서 사용자에게 웹페이지를 보여주는 영역을 기준으로 한 x, y 좌표(clientX, clientY)를 살펴볼 수 있다.
4. 버블링과 캡쳐링 이벤트 전달/차단
❗ 버블링 방식 : 자식에서 → 부모 노드로 올라가며 이벤트가 실행
❗ 캡쳐링 방식 : 부모에서 → 자식 노드로 내려가며 이벤트가 실행
4-1. 버블링
<body>
<div onclick="alert('1번 div');" class="div-test div1">
<div onclick="alert('2번 div');" class="div-test div2">
<div onclick="alert('3번 div');" class="div-test div3">
<div onclick="alert('4번 div');" class="div-test div4"></div>
</div>
</div>
</div>
</body>
- 버블링이란 한 요소에 이벤트가 발생하면 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작되는 것을 일컫는다(자식 → 부모 노드).
- 가장 최상위 조상 요소를 만날 때까지 이 과정이 반복되며, 거슬러 올라가는 동안 요소 각각에 할당된 핸들러가 제 차례에 동작한다.
- 예시에서 최고 조상에 해당하는 div1을 누르거든 스스로에게 할당된 이벤트 핸들러로서 alert()를 띄우고 마친다. 하지만 가장 안쪽에 자리한 div4를 클릭하면 4, 3, 2, 1순으로 연달아 호출됨을 확인할 수 있다.
그렇다면 버블링은 어떤 때에 활용할 수 있을까? 예를 들어 온라인 쇼핑몰에서 상품을 클릭한다고 가정해보자. 사진, 설명 등등 페이지 구성 요소가 무척 다양할 것이다. 그러한 것들을 일일이 구현하는 대신에 전체 페이지 대상으로 이벤트 할당하는 방법을 생각해 볼 수 있다. 실제 클릭한 건 이미지이지만, 이미지 바깥에 걸려있는 이벤트 핸들러 또한 호출된 듯 작동하도록 말이다.
4-2. 버블링 차단 event.stopPropagation()
이벤트객체.stopPropagation() : 익스플로러 제외한 브라우저window.event.cancelBubble=true; : 익스플로러
event.target | 실제 이벤트가 시작된 타깃 요소 ※ 버블링 진행 중에도 변함 없음 |
event.currentTarget | 현재 실행 중인 핸들러가 할당된 요소 ※ 버블링 진행 과정 따라 계속 변경 |
<body>
<div onclick="bubble(event);" class="div-test div1">
<div onclick="bubble(event);" class="div-test div2">
<div onclick="bubble(event);" class="div-test div3">
<div onclick="bubble(event);" class="div-test div4"></div>
</div>
</div>
</div>
<script>
function bubble(event) {
console.log("event.target");
console.log(event.target); //4 4 4 4
console.log("event.currentTarget");
console.log(event.currentTarget); //4 3 2 1
event.stopPropagation();
}
</script>
</body>
- 이벤트 객체 메소드인 stopPropagation()은 버블링 중단을 명령한다. 즉 이벤트가 전파돼 모든 조상 이벤트가 줄줄이 발생되던 것을 차단한다.
- event.target은 실제 이벤트가 시작된 타깃 요소, 이곳 예시에서는 실제 onclick 된 요소를 가리킨다. 중단 명령이 실행되기 전 가장 안쪽에 위치한 자식 요소 div4번을 네 번 연달아 누르면 해당 요소를 클릭 횟수 그대로 반환한다.
- 한편 event.currentTarget은 현재 실행 중인 핸들러가 할당된 요소를 말한다. 따라서 시작은 자식 요소로부터 생겨났지만, 다음 조상과 그 다음 조상으로 거슬러 올라가는 동안 가리키는 값이 변하게 마련이다.
- 결과적으로 버블링 중단이 선언되자 상위 요소로 전파되는 것 없이 단 한 번 실행되고 멈춘다: event.stopPropagation()
4-3. 캡쳐링 true
<body>
<div class="div-test div1 capture">
<div class="div-test div2 capture">
<div class="div-test div3 capture">
<div class="div-test div4 capture"></div>
</div>
</div>
</div>
<script>
let divs = document.querySelectorAll(".capture");
console.log(divs); //NodeList
divs.forEach(function(item){
console.log(item);
item.addEventListener('click', () => alert('캡쳐링 : ' + item.className), true);
item.addEventListener('click', () => alert('버블링 : ' + item.className));
</script>
</body>
- 캡쳐링은 이벤트가 하위 요소로 전파되는 것을 말한다. 즉 버블링의 반대로 기능하며, 필요에 따라 의도적으로 설정할 수 있다.
- 캡쳐링 단계에서 이벤트를 잡아내기 위해서는 addEventListener의 인자인 capture 옵션을 true로 명시해야 한다.
- 위 예시에서도 볼 수 있듯 캡쳐링은 세 번째 인자로서 true를 전달하고, 명시하지 않은 경우는 기본값인 버블링으로 인식된다.
5. 기본 이벤트 제거
❗ 기본 이벤트란, 태그 중 이벤트 핸들러를 기본적으로 가지고 있는 것들을 말한다. ① <a> 태그를 통한 페이지 이동, ② <input type="submit"> 시 입력 양식 제출 후 페이지 갱신 등과 같은 브라우저 기본 동작을 의미한다.
태그에 기본적으로 설정돼 있는 이벤트를 제거하는 방법으로는 다음 두 가지가 있다.
5-1. event.preventDefault()
<a href="test.html" onclick="event.preventDefault()">클릭해도 절대 이동할 수 없는 a 태그</a>
- 클릭해도 관련 이벤트가 절대 동작하지 않는 태그임을 명시한다: onclick="event.preventDefault()"
5-2. return false;
<body>
<form onsubmit="return invalidate();">
<label for="password">비밀번호 : </label>
<input type="password" name="password" id="password" required>
<br>
<label for="checkPwd">비밀번호 확인 : </label>
<input type="password" name="checkPwd" id="checkPwd" required>
<input type="submit" value="제출">
</form>
<script>
function invalidate(){
let pwd1 = document.getElementById('password').value;
let pwd2 = document.getElementById('checkPwd').value;
if(pwd1 == pwd2) {
alert('입력 비밀번호가 일치합니다');
} else {
alert('입력 비밀번호가 일치하지 않습니다');
document.getElementById('checkPwd').select();
return false;
}
}
</script>
</body>
- onevent에서 할당된 이벤트 핸들러의 리턴값을 false로 설정해 기본 이벤트를 제거하는 방법이다.
- <form> 작성 후 제출(submit) 시에 비밀번호란과 비밀번호 확인란에 각각 기입된 값이 같은지 확인하고자 하는 예시이다. 따라서 별도 함수를 작성하고 <form> 태그 안에서 이벤트 핸들러 처리한다: <form onsubmit="return invalidate();">
- 선언된 함수 invalidate()는 올바른 값이 입력됐는지, 비밀번호 확인 결과 동일한지를 판단한다.
- 요소 접근 방식 통해 일반 변수에 해당 요소들의 값(value)을 알아와 저장한다.
- 비밀번호란과 비밀번호 확인란에 쓰인 값들의 일치 여부를 조건문 if문 통해 처리한다. 특히 일치하지 않는 경우에 대해서는 해당 값을 선택(select)하고, false를 반환(return false)하도록 작성한다.
- 결과적으로
<input type="submit"> 시 입력 양식 제출 후 페이지 갱신하도록 되어 있는 기본 이벤트대신에 비밀번호 확인란이 선택(select)된 모습을 확인할 수 있다.
'Frontend > JavaScript' 카테고리의 다른 글
[JavaScript] 마우스 이벤트 | 키보드 이벤트 | 스크롤 이벤트 | 폼 이벤트 (0) | 2022.02.23 |
---|---|
[JavaScript] BOM | window | setInterval | 타이머 메소드 (0) | 2022.02.22 |
[JavaScript] 문서 수정 | 노드 메소드 | insertAdjacentHTML | classList (0) | 2022.02.22 |
[JavaScript] 주요 노드 프로퍼티 | innerHTML | textContent | value (0) | 2022.02.21 |
[JavaScript] 요소 검색 메소드 | querySelector | NodeList (0) | 2022.02.21 |