JavaScript

이벤트 위임 (event delegation)

ㅇㄱ9 2022. 5. 10. 18:00
728x90

이벤트 위임

이벤트 위임은 캡쳐링과 버블링을 이용한 것으로, 여러 엘리먼트마다 각각 이벤트 핸들러를 할당하지 않고, 공통되는 부모에 이벤트 핸들러를 할당하여 이벤트를 관리하는 방식이다.

 

1) 여러 개의 자식 엘리먼트 이벤트를 한 번에 관리 

: HTML의 읽기전용 속성인 데이터 속성을 사용하여 이벤트 위임을 구현할 수 있다. 

 

데이터 속성

여기서 데이터 속성은 화면에 안 보이게 글이나 추가 정보를 엘리먼트에 담을 수 있는 속성으로 data- 로 시작하는 속성이다.  

<article
  id="electriccars"
  data-columns="3"
  data-index-number="12314"
  data-parent="cars">
...
</article>

위처럼 html을 작성하면 각각의 데이터 속성을 읽는 방법은 아래와 같다. 

(대시는 camelCase로 변환된다.)

var article = document.getElementById('electriccars');

article.dataset.columns // "3"
article.dataset.indexNumber // "12314"
article.dataset.parent // "cars"

속성을 바꾸기 위해서는 article.dataset.columns = 5 와 같이 작성해주면 된다.

 

데이터 속성은 Html속성이기 때문에 CSS에서도 접근이 가능한데, 위의 예시에서 data-parent를 article에 보여주기 위해서는 아래와 같이 작성하면 된다. 

article::before {
  content: attr(data-parent);
}

이때 아래와 같이 attribute selector를 사용하여 데이터에 따른 스타일을 변경해 줄 수 있다. 

article[data-columns='3'] {
  width: 400px;
}
article[data-columns='4'] {
  width: 600px;
}

 

이를 이용하여 서로 다른 역할을 하는 세 개의 버튼의 이벤트를 어떻게 이벤트 위임 방식으로 처리하는지 살펴보자. 

<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Example</title>
</head>
<body>
    <div id="Menu">
        <button data-action="save">저장하기</button>
        <button data-action="reset">초기화 하기</button>
        <button data-action="load">불러오기</button>
    </div>
    <script src="app.js"></script>
</body>
</html>
const $Menu = document.getElementById('Menu')

const ActionFunctions = {
  save: () => alert('저장하기'),
  reset: () => alert('초기화하기'),
  load: () => alert('불러오기'),
}

$Menu.addEventListener('click', e => {
  const action = e.target.dataset.action
  if (action) {
    ActionFunctions[action]()
  }
})

[출처]https://velog.io/@moonheekim0118/JavaScript-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%B2%84%EB%B8%94%EB%A7%81

 

2) 동적 엘리먼츠에 대한 이벤트 관리하기 

: 동적으로 추가되거나 삭제하는 엘리먼트에 매번 이벤트 리스너를 추가하고 삭제한다면 코드의 효율성을 낮추고 리스터가 제대로 삭제되지 않는 경우 메모리 누수의 가능성도 커진다. 이때 이벤트 위임의 방식을 사용하여 더 나은 코드를 작성할 수 있다. 

 

아래의 예는 itemList에 두가지 항목을 넣고 자바스크립트 코드에서 각각의 input에 click이벤트 리스너를 달아주는 코드이다. 

<h1>오늘의 할 일</h1>
<ul class="itemList">
	<li>
		<input type="checkbox" id="item1">
		<label for="item1">이벤트 버블링 학습</label>
	</li>
	<li>
		<input type="checkbox" id="item2">
		<label for="item2">이벤트 캡쳐 학습</label>
	</li>
</ul>
var inputs = document.querySelectorAll('input');
inputs.forEach(function(input) {
	input.addEventListener('click', function(event) {
		alert('clicked');
	});
});

이때 이상태에서 자바스크립트 코드로 아이템을 아래와 같이 추가하게 된다면 문제가 발생한다. 

// 새 리스트 아이템을 추가하는 코드
var itemList = document.querySelector('.itemList');

var li = document.createElement('li');
var input = document.createElement('input');
var label = document.createElement('label');
var labelText = document.createTextNode('이벤트 위임 학습');

input.setAttribute('type', 'checkbox');
input.setAttribute('id', 'item3');
label.setAttribute('for', 'item3');
label.appendChild(labelText);
li.appendChild(input);
li.appendChild(label);
itemList.appendChild(li);

새로 추가된 아이템에도 이벤트 리스너가 등록되길 원했지만 이벤트 리스너가 등록되는 시점에는 리스트 아이템이 기존의 2개만 존재하기 때문에 추가된 아이템에는 등록되지 않음을 확인할 수 있다. 

 

이때 각각의 아이템이 추가될때마다 이벤트 리스너를 달아주는 것보다 상위 요소(예시에서는 itemList)에 이벤트 리스너를 추가해줌으로써 하위 요소의 이벤트를 감지하게 하는 방법이 좋다. 

var itemList = document.querySelector('.itemList');
itemList.addEventListener('click', function(event) {
	alert('clicked');
});

정리

이벤트 위임은 다음과 같은 알고리즘으로 동작한다. 

  1. 컨테이너에 하나의 핸들러를 할당합니다.
  2. 핸들러의 event.target을 사용해 이벤트가 발생한 요소가 어디인지 알아냅니다.
  3. 원하는 요소에서 이벤트가 발생했다고 확인되면 이벤트를 핸들링합니다.

- 장점

  • 많은 핸들러를 할당하지 않아도 되기 때문에 초기화가 단순해지고 메모리가 절약됩니다.
  • 요소를 추가하거나 제거할 때 해당 요소에 할당된 핸들러를 추가하거나 제거할 필요가 없기 때문에 코드가 짧아집니다.
  • innerHTML이나 유사한 기능을 하는 스크립트로 요소 덩어리를 더하거나 뺄 수 있기 때문에 DOM 수정이 쉬워집니다.

- 단점

  • 이벤트 위임을 사용하려면 이벤트가 반드시 버블링 되어야 합니다. 하지만 몇몇 이벤트는 버블링 되지 않습니다. 그리고 낮은 레벨에 할당한 핸들러엔 event.stopPropagation()를 쓸 수 없습니다.
  • 컨테이너 수준에 할당된 핸들러가 응답할 필요가 있는 이벤트이든 아니든 상관없이 모든 하위 컨테이너에서 발생하는 이벤트에 응답해야 하므로 CPU 작업 부하가 늘어날 수 있습니다. 그런데 이런 부하는 무시할만한 수준이므로 실제로는 잘 고려하지 않습니다.

--- 참고 사이트 

- 이벤트 위임

- [JavaScript] 이벤트 버블링, 캡쳐링, 위임

[JavaScript] 이벤트 위임

- 데이터 속성 사용하기 

 

728x90
반응형