2019. 06. 05 수정


1. 이벤트 핸들링 (Event Handling)

JS 코드의 대부분은 이벤트(event)에 의해 동작합니다.

이벤트란 대표적으로 클릭, 키보드 입력 등 사용자의 어떤 행위를 의미합니다.

이러한 이벤트를 처리하는 것을 이벤트 핸들링이라고 합니다.


이벤트 핸들링은 다음과 같은 과정으로 이뤄집니다.

  1. 이벤트를 받아줄 요소를 선택합니다.
  2. 그 요소가 어떤 이벤트에 반응할지, 즉 요소와 이벤트를 연결해주는 바인딩을 합니다.
  3. 이벤트가 발생했을 때 실행될 코드를 작성합니다.

이제 이러한 과정을 살펴보도록 하겠습니다.





2. 바인딩( Binding )

요소와 이벤트를 연결해주는 바인딩을 하는 방법에는 여러가지가 있습니다.


1) HTML 이벤트 핸들러

HTML 요소의 onclick 속성에 JS 함수를 호출하여 바인딩하는 방법입니다.

<button class="btn" onclick="myFunction()">

해당 버튼을 클릭하면 작성해놓은 myFunction() 함수가 실행될 것입니다. ( myFunction()은 따로 구현을 해야겠죠? )


그런데 HTML과 JS코드는 분리시켜 관리하는 것을 권장하기 때문에 이 방식은 좋은 방법이 아닙니다.




2) DOM 이벤트 핸들러

DOM 요소에 이벤트를 바인딩하고 이벤트가 발생하면 실행될 코드를 함수로 작성하는 방법입니다.

element.on이벤트이름 = myFunction(){...}

여기서 "on이벤트이름"이란 JS 이벤트 명에 on을 붙인 것입니다.

예를 들어, click 이벤트를 바인딩 하려면 onclick이 되겠고, blur 이벤트를 바인딩 하려면 onblur가 될 것입니다.


이 방법도 그렇게 권장하는 방식은 아닙니다.




3) Dom 이벤트 리스너

DOM 요소에 addEventListener 메서드를 호출하여 이벤트를 바인딩하고, 수행할 함수를 작성합니다.

element.addEventListener("이벤트이름", myfunction(){ ... }, [, boolean]);

해당 방식이 바닐라 JS 기준으로 가장 많이 사용되는 방식입니다.


"2) DOM 이벤트 핸들러" 방식과는 달리 "이벤트이름"에는 원래 JS 이벤트명을 작성하면 됩니다.

예를 들어 click 이벤트라면 click을 그대로 작성하면 됩니다.


3번째 매개변수는 이벤트 흐름을 true로 할 것인지 false로 할 것인지 작성하는데요, 일반적으로 false를 사용합니다.

이벤트 흐름에 대해서는 이 글 마지막에 다루도록 하겠습니다.





3. 동적으로 요소 생성시 이벤트 바인딩 이슈 - rebinding

위와 같이 이벤트 바인딩 코드를 작성하게 되면, HTML 문서가 로딩될 때 해당 요소마다 이벤트가 바인딩이 될 것입니다.

그리고 그 요소에 등록한 이벤트가 사용자에 의해 발생되기를 기다리겠죠.


그런데 createTextNode() 메서드 또는 jQuery의 append(), after() 메서드와 같이 동적으로 요소를 생성할 경우에는 이벤트 바인딩이 적용되지 않습니다.

<ul>
<li class="foo">item 1</li>
<li class="foo">item 2</li>
<li class="foo lastItem">item 3</li>
</ul>

<button class="goo">ajax 요청</button>


<script>
$(".foo").click(function(){
console.log("item이 클릭됨")
})


$(".goo").click(function(){
$.ajax({
// ajax 요청
...

success: function(result) {
$(".lastItem").after("<li class="foo">item 4</li>")

// click event rebinding
$(".foo").on("click", function(){
console.log("item이 클릭됨")
})
}
})
})
</script>

위의 코드는 "ajax 요청" 버튼을 클릭했을 때, item 4를 item 3 아래에 추가하는 코드입니다.

( 코드는 간단히 하기 위해 jQuery를 이용했습니다. )


문서가 최초 로딩된 후, 그 이후에 동적으로 요소(item4)를 생성하면 해당 요소는 이벤트 바인딩이 되지 않습니다.

즉, 새로 생성된 item 4는 바인딩이 되어 있지 않기 때문에 click 이벤트를 처리할 수 없습니다.


따라서 위의 예제처럼 동적으로 추가한 새로운 요소는 이벤트를 재바인딩(rebinding) 해야 합니다.





4. 이벤트 흐름 - Event Bubbling(이벤트 버블링) , Event Chapturing(이벤트 캡쳐링)

HTML의 어떤 요소는 또 다른 어떤 요소의 내부에 속해있습니다.

DOM 구조에서 최상위 요소는 document이고 그 아래에 head , body , a , ul , li 등이 있습니다.

즉, 모든 요소는 다른 어떤 요소에 속해있는 트리 구조입니다.


그래서 어떤 요소에 대해 어떤 이벤트가 발생하면, 그 요소의 부모도 같은 이벤트의 영향을 받게됩니다.

이러한 이벤트 흐름을 Event Bubbling이라고 합니다.

<ul id="a">
<li id="b">
<button id="c">alert 발생</a>
</li>
</ul>

<script>
function showElement() {
alert(this.innerHTML);
}
el = document.getElementById("a");
el.addEventListener('click', showElement, false);

el = document.getElementById("b");
el.addEventListener('click', showElement, false);

el = document.getElementById("c");
el.addEventListener('click', showElement, false);
</script>

위의 예제는 id 속성 값 a, b, c에 대해 click 이벤트를 바인딩했을 때, a, b, c 중 어느 요소를 클릭하느냐에 따라 alert 창이 몇 번 발생하는지를 보여줍니다.

예를 들어, c를 클릭하면 이벤트 버블링되어, b, a도 클릭이 되는 효과가 있어 총 3번의 alert 창이 발생합니다.

b를 클릭하면 a가 이벤트 버블링되어, 총 2번의 alert 창이 발생합니다.


이와 반대되는 개념으로 Event Capturing이 있습니다.

Event Capturing은 자식으로 이벤트가 연계되는 이벤트 흐름을 말합니다.


DOM 리스너( addEventListener )에서 3번째 매개변수의 값의 의미는 다음과 같습니다.

  • false
    • Event Bubbling
  • true
    • Event Capturing

일반적으로 bubbling을 사용하기 때문에 false를 사용합니다.




이상으로 DOM 요소에 이벤트를 바인딩 했을 시, 겪을 수 있는 이슈들에 대해 알아보았습니다.

  • 바인딩 하는 방법에는 여러가지가 있는데, addEventListner()를 선호한다.
  • 동적으로 요소 생성시, 재바인딩(rebinding)이 필요하다.
  • DOM 요소는 항상 상위 요소가 존재하는데, 이벤트는 상위 요소로 버블링/캡쳐링 된다. ( Event Bubbling, Event Capturing )