해당 글은 1/12에 노션으로 작성한 걸 2부로 나눈 것입니다. 다음 포스팅은 Debounce, Throttle, 이벤트 시뮬레이션을 다룰 것입니다.
웹페이지에서 마우스를 클릭했을 때, 키를 입력했을 때, 특정요소에 포커스가 이동돼었을 때 어떤 사건을 발생시키는 것
대부분 상호작용이 되는 곳은 전부 이벤트가 활용된다.
기본적인 이벤트 종류 (자세한 내용은 아래 사이트에서 쉽게 확인할 수 있다.)
이벤트 종류 확인하기
- UI 이벤트 : load, scroll…
- 키보드 이벤트 : keydown, keyup
- 마우스 이벤트 : click, mouseover, mousedown…
- 폼 요소 이벤트 : submit, focus…
- CSS 이벤트 : transitioned
이벤트가 발생하면 브라우저는 이벤트 객체라는 것을 만든다. 여기에 이벤트에 관한 상세한 정보를 넣은 다음, 핸들러에 인수 형태로 전달한다.
addEventListener
앞에 기술된 객체를 가리키는 것-> 여기서 좌표는 모니터 기준 좌표가 아니라 브라우저 화면 기준 좌표이다!
addEventListener()
메소드 활용하기addEventListener
메소드를 이용하여 대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 지정한다.결론만 말하면 button.addEventListener('click', function() { handleClick(); }); 코드와 button.addEventListener('click', handleClick); 코드의 차이점이 뭘까?
페이지에 존재하는 이벤트 핸들러의 개수가 페이지 성능에 직접적인 영향을 미친다.
가령 input에 계속 동작한다던지, scroll 이벤트, resize 이벤트마다 발생하는 핸들러가 여러 개라면? omg..
문서에서 요소를 제거하거나 페이지를 떠나는 경우, 요소는 제거되지만 이벤트 핸들러는 남아 메모리를 점유한다. 요소를 제거할 것이라면 잔류 핸들러를 직접 제거하는 것이 좋고 그러기 위해서는 떠나기 전에 onunload 이벤트 핸들러를 이용하여 잔류 핸들러를 모두 제거하는 것이 좋다.
단, onunload 이벤트 핸들러를 사용하면 페이지가 bfcache에 저장되지는 않으므로 잘 선택해야 한다.
💡 bfcache?
back/forward cache의 약자로 이전/다음으로 이동할 때, 페이지 전체를 캐싱하여 페이지를 로드하는 시간과 데이터를 절약하는 기법
위에서 설명했다시피 계층적 구조에 포함되어 있는 HTML요소에 이벤트가 발생할 경우 연쇄적 반응이 일어나는 것
자식 요소에서 발생한 이벤트가 부모 요소로 전파되는 것
즉, 특정 화면 요소에서 이벤트가 발생하면 해당 이벤트가 상위의 화면 요소들로 전달되어 가는 특성을 가진다.
자식 요소에서 발생한 이벤트가 부모 요소부터 시작하여 이벤트를 발생시킨 자식 요소까지 도달하는 것
즉, 이벤트가 발생했을 때 해당 이벤트가 최상위 요소인 body부터 해당 이벤트가 발생한 태그까지 전달되며 내려가는 특징을 가진다.
일반적으로 이벤트는 후술할 흐름에서 보면 알겠지만 캡처링 → 버블링 방식으로 전파되지만 대부분의 경우 우리는 버블링 단계에서 이벤트를 처리한다.
🔥 이벤트 핸들러 등록 방식에 따른 전파 차이이벤트 핸들러 어트리뷰트, 프로퍼티 방식 : 이 방식을 이용한 경우 버블링 단계의 이벤트만 캐치할 수 있다.
addEventListener 이용 방식 : 모든 단계의 이벤트를 캐치할 수 있고, 3번째 매개변수는 캡처링 이벤트를 캡처링할 건지 결정하는데 쓰인다. 기본은 false지만 true일 경우 캡처링 단계의 이벤트를 캐치할 수 있다.
계층적 구조에 포함되어 있는 HTML 요소에 이벤트가 발생할 경우 연쇄적 반응이 일어난다. 즉, 이벤트가 전파되는데 전파 방향에 따라 버블링(Event Bubbling)과 캡처링(Event Capturing)으로 구분할 수 있다. 주의할 것은 버블링과 캡처링은 둘 중에 하나만 발생하는 것이 아니라 캡처링부터 시작하여 버블링으로 종료한다는 것이다. 즉, 이벤트가 발생했을 때 캡처링과 버블링은 순차적으로 발생한다. 흐름을 보면 다음과 같다.
전파가 왜 있을까? 논리적으로는 자식 요소가 부모 요소 영역 안에 위치하고 있기 때문에 자식 요소만을 클릭했다 하더라도 부모 요소도 클릭했다고 넓은 영역에서 보면 맞는 말이기 때문… (그 A는 B이고 B는 C면 A는 C이다 같이…), 이벤트 위임과 같은 기법을 사용하면 이벤트를 일일이 등록하지 않아도 되므로 성능적으로 좋기 때문
만일 부모와 자식 둘 다 이벤트를 등록한 상태에서, 자식 요소만 클릭했을 때만 이벤트 발생하고 부모 요소는 이벤트를 발생시키고 싶지 않은 상황에서 이벤트 동작 자체를 바꿀 수 없으므로 이벤트 전파를 방지 처리를 하는 식으로 해결해야 한다.
전파 방지는 필요한 경우가 아니면 죽은 영역이 될 수 있는 위험이 있으므로 아키텍처를 잘 고려해서 사용해야 한다.
버블링 또는 캡처링 설정에 따라 상위, 하위로 가는 이벤트 전파를 막을 수 있다. 즉, 각 엘리먼트의 이벤트 리스너만 동작할 수 있게 해준다.
이벤트 전파와 더불어 형제 이벤트 실행을 중지한다. 동일한 child 요소의 이벤트 리스너가 2개 등록 되어 있을 때, 어떠한 조건에서 클릭 이벤트를 두 번 실행하지 않고 한 번만 실행토록 하길 원한다면 유용하다.
위의 e.stopPropagation()
은 그 함수 자체의 전파는 막아주지만 다른 형제 핸들러들이 동작하는 건 막지 못하므로 요소에 할당된 다른 핸들러의 동작도 막으려면 해당 함수를 쓰는 것이 좋다.
정교하게 하고 싶을 때 직접 조건 분기를 통해 일일이 지정해 주는 방식이다.
이벤트 전파 , 형제 이벤트 실행 중지 뿐만 아니라 기본 이벤트 동작 자체를 취소한다.
다수의 자식 요소에 각각 이벤트 핸들러를 바인딩하는 대신 하나의 부모 요소에 이벤트 핸들러를 바인딩하는 방법, DOM 트리에 새로운 자식 태그 요소를 추가하더라도 이벤트 처리는 부모 요소인 태그 요소에 위임되었기 때문에 새로운 요소에 이벤트를 핸들러를 다시 바인딩할 필요가 없다.
왜 사용해야 할까?
React
JSX를 사용하여 이벤트 핸들러를 직접 작성하고, event.target을 이용하여 이벤트 처리한다.
Vue.js
v-on 지시어를 사용하여 이벤트 핸들러를 등록하고, $event 객체를 통해 이벤트 정보에 접근한다.