TIL24⎟JavaScript : 반복되어지는 EVENT를 처리해야할 때, event delegation

itssweetrain·2021년 1월 30일
2

JavaScript 

목록 보기
18/25
post-thumbnail

https://ko.javascript.info/event-delegation

event object

이벤트를 제대로 다루려면 어떤 일이 일어났는지 상세히 알아야 한다. ‘click’ 이벤트가 발생했다면 마우스 포인터가 어디에 있는지, ‘keydown’ 이벤트가 발생했다면 어떤 키가 눌렸는지 등에 대한 상세한 정보가 필요하다.

이벤트가 발생하면 브라우저는 이벤트 객체(event object)라는 것을 만든다. 여기에 이벤트에 관한 상세한 정보를 넣은 다음, 핸들러에 인수 형태로 전달합니다.

이벤트 객체로부터 포인터 좌표 정보를 얻어내는 예시

<input type="button" value="클릭해 주세요." id="elem">

<script>
  elem.onclick = function(event) {
    // 이벤트 타입과 요소, 클릭 이벤트가 발생한 좌표를 보여줌
    alert(event.type + " 이벤트가 " + event.currentTarget + "에서 발생했습니다.");
    alert("이벤트가 발생한 곳의 좌표는 " + event.clientX + ":" + event.clientY +"입니다.");
  };
</script>


이벤트 객체에서 지원하는 프로퍼티 예시

  • event.type
    이벤트 타입, 위 예시에선 "click".
  • event.currentTarget
    이벤트를 처리하는 요소. 화살표 함수를 사용해 핸들러를 만들거나 다른 곳에 바인딩하지 않은 경우엔 this가 가리키는 값과 같음, 화살표 함수를 사용했거나 함수를 다른 곳에 바인딩한 경우엔 event.currentTarget를 사용해 이벤트가 처리되는 요소 정보를 얻을 수 있음
  • event.clientX / event.clientY
    포인터 관련 이벤트에서, 커서의 상대 좌표(모니터 기준 좌표가 아닌, 브라우저 화면 기준 좌표)

event.preventDefault();

브라우저에게 실행시켜야할 것을 멈추게 하는것
checkbox의 box를 클릭하면 체크가 되어야하는데 체크가 되지 않는 것
form을 submit하려고 enter키를 눌렀지만 전송되지 않는 것

⭐️ event delegation

이벤트 위임을 사용하면 요소마다 핸들러를 할당하지 않고, 요소의 공통 조상에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있다.

❗️ 반복되어지는 event를 처리해야할 때, 즉 부모 안에 있는 자식들에게 공통적으로 무언가 처리를 해야할 때, 일일이 이벤트 리스너를 자식 노드에 추가하는 것보다, 부모에 등록하는 것이 좋다.target이 등록되어 있기 때문에 원하는 target에만 처리를 해 줄 수 있기 때문에 event delegation을 쓰는 것이 좋다.
자식요소들이 배열 형태로 들어있는 경우 유용!

다양한 이벤트나 사용자에게 보여지는 것을 만드는 것이 front end의 본질이므로! 사용자에게 조금 더 가까이 UI를 만드는 것이 좋다!

*ul안에 10개의 li에, 클릭되면 .selected를 이용해서 효과를 주고 싶을 때

/*Bad*/
const lis = document.querySelectorAll('li');
lis.forEach(li => {
li.addEventListener('click', ()=>{
li.classList.add('selected');
});
});

❗️ querySelectorAll은 배열(nodelist)을 리턴하고 개인적으로 선택하려면 forEach구문을 사용해야한다. 또한 nodeList를 Array로 만들려면 Array.from()을 사용해야 한다. 하지만 배열은 addEventListner를 추가할 수 없다.

/*Coooool*/
const ul = document.querySelector('ul');
ul.addEventListener('click, event() => {
if(event.target.tagName == 'LI'){
event.target.classList.add('selected');
}
});

Bubbling

부모 컨테이너(div) 안에 버튼(button) 5개가 있다고 가정.

div에 클릭 이벤트 리스너를 등록해 놓고, button에도 클릭 이벤트 리스너를 등록해 놓으면, button을 클릭할 때 button에 등록된 클릭 이벤트 콜백함수가 실행되어지고, div에도 등록된 클릭 이벤트 콜백 함수가 실행되어진다.

div에 클릭이 일어 난게 아닌대도 div에 등록된 콜백함수가 실행되는 것은 자식 요소에게 이벤트가 발생하면 부모에게 동일한 이벤트를 위해 등록된 콜백함수도 실행되어지는 것.

이것이 버블링! 이벤트가 위쪽 (부모쪽)으로 올라가는 것.

이제, 이 버블링을 이용해서 이벤트 위임을 구형할 수 있다.

button 5개 모두에게 클릭 이벤트 리스너를 등록하지 않아도, 부모 요소 div에만 클릭 이벤트 리스너를 등록 해놓으면 그 안의 버튼들이 클릭이 되어지면 부모요소에 등록된 클릭 이벤트가 발생한다.

여기서 문제는, 버튼을 클릭해도, 또는 div안의 여백의 공간을 클릭해도 등록된 클릭 이벤트 콜백 함수가 계속 실행되어진다. 버튼이 클릭되어지면 자식 요소가 클릭이 되었으니 버블링을 통해 div에 등록된 콜백 함수가 실행되고 div안의 여백이 클릭되면 div 자체가 클릭이 되었으니 콜백 함수가 실행되어지고.

여기서 버튼이 클릭 되었을때만 이벤트를 처리 하고 싶다면?

event.target.tagName
event.target을 이용해서 현재 클릭된 요소가 버튼일때만 처리해주면 된다.

elem.closest(selector)

elem의 상위 요소 중 selector와 일치하는 가장 근접한 조상 요소를 반환

profile
motivation⚡️

0개의 댓글