ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] 이벤트(Event)
    Front-End(Web)/Javascript 2021. 3. 16. 13:56
    반응형

    React의 이벤트에 대해 포스팅하면서, Javascript에서의 이벤트 핸들링과 흐름, 관련된 개념들 역시 정리할 필요성이 있었다.

    Javascript 관련된 내용을 분리했으며, 이를 토대로 이벤트 위임까지 한번 정리해보도록 하겠다.


    📒 Event Handling

    React 합성 이벤트의 다양한 attribute들을 알아보면서, 이벤트가 기본적으로 어떻게 브라우저에서 감지되고 전파되는지 공부할 필요를 느꼈다.

    그래야, bubbles의 개념이나, eventPhase의 각 페이즈를 이해할 수 있겠다고 생각했다.

     

    1. 이벤트 핸들링(Handling)

    이벤트는 마우스 클릭이나 키보드 입력같이 일반적으로 사용자가 행하는 모든 동작을 일컫는다.

    이러한 이벤트를 원하는데로 처리하는것을 이벤트 핸들링 이라고 한다.

     

    - Javascript DOM 이벤트 종류

     

    마우스 이벤트:

    • click – 요소 위에서 마우스 왼쪽 버튼을 눌렀을 때(터치스크린이 있는 장치에선 탭 했을 때) 발생합니다.
    • contextmenu – 요소 위에서 마우스 오른쪽 버튼을 눌렀을 때 발생합니다.
    • mouseover와 mouseout – 마우스 커서를 요소 위로 움직였을 때, 커서가 요소 밖으로 움직였을 때 발생합니다.
    • mousedown과 mouseup – 요소 위에서 마우스 왼쪽 버튼을 누르고 있을 때, 마우스 버튼을 뗄 때 발생합니다.
    • mousemove – 마우스를 움직일 때 발생합니다.

    폼 요소 이벤트:

    • submit – 사용자가 <form>을 제출할 때 발생합니다.
    • focus – 사용자가 <input>과 같은 요소에 포커스 할 때 발생합니다.

    키보드 이벤트:

    • keydown과 keyup – 사용자가 키보드 버튼을 누르거나 뗄 때 발생합니다.

    문서 이벤트:

    • DOMContentLoaded – HTML이 전부 로드 및 처리되어 DOM 생성이 완료되었을 때 발생합니다.

    CSS 이벤트:

    • transitionend – CSS 애니메이션이 종료되었을 때 발생합니다

     

    - 이벤트 핸들러

     

    이벤트에 반응하려면 이벤트가 발생했을 때 실행되는 함수를 할당해야 한다. 이 함수를, 핸들러(handler)라고 칭한다.

    이벤트 핸들러를 이벤트를 적용할 DOM과 연결시켜주어야 실행될 수 있다. 이를, 뒤의 이벤트 바인딩에서 알아보자.

     

     

    2. 이벤트 바인딩(Binding)

    앞서 언급했듯, 우리가 이벤트를 적용할 DOM요소와 핸들러 함수를 연결해주었는데, 이를 이벤트 바인딩(Binding)이라고 한다.

    이벤트 바인딩의 3가지 방법에 대해 알아보자!

     

    1) HTML 속성 할당

    <button onclick="alert("클릭!")">버튼</button>

    가장 기본적인 방법이다. 대상 DOM의 on[event] 속성과, 그 값으로 함수를 작성해주면 된다.

    가장 비선호되는 방법으로, HTML 중간에 Javascript 로직이 포함되어 코드량과 가독성이 안좋아지기 때문이다.

     

     

    2) DOM 프로퍼티

    <button class="click-btn">버튼</button>
    
    document.queryselector(".click-btn").onclick = function() {
      alert("클릭2");
    }

    두 번째는 바로, 이처럼 DOM을 선택한 뒤 해당 프로퍼티로 핸들러 함수를 할당하는 방법이다.

    가독성은 나아졌으나, 두 방법 모두의 단점은 바로 복수의 핸들러를 할당할 수 없다는 것이다. 

     

    3) addEventListener

    <button class="click-btn">버튼</button>
    
    document.queryselector(".click-btn").addEventListener("click", handleClick);
    
    function handleClick() {
      alert("클릭3");
      console.log("클릭 콘솔");
    }

    Javascript의 대표적인 바인딩으로 addEventListener를 통용한다. 문법은 아래와 같으며, 인자로 이벤트와 핸들러를 각각 넣어준다.

    * element.addEventListener(event, function, [options])

     

    옵션 종류는 아래가 있다.

    • once: true이면 이벤트가 트리거 될 때 리스너가 자동으로 삭제됩니다.
    • capture: 이벤트 버블링/캡쳐링을 결정하는 프로퍼티로, false(bubbling)이 기본값이다.
    • passive: true이면 리스너에서 지정한 함수가 preventDefault()를 호출하지 않습니다. 

     

    3. 이벤트 객체(e)

    이벤트가 발생하면 브라우저는 이벤트 객체(event object)를 만든다.

    여기에 이벤트에 관한 상세 정보를 넣어 전달하고, 핸들러에서는 이를 key - value 로 불러와서 인식 및 활용하는 것이다.

    대표적인 이벤트 객체 프로퍼티는 아래가 있다.

    • e.type : 이벤트 타입. click, focus, scroll 등.
    • e.target : 이벤트가 바인딩된 DOM에서, 이벤트가 직접적으로 발생한 DOM을 반환한다. (자식요소가 반환될 수도 있음)
    • e.currentTarget : 이벤트가 바인딩된 DOM 을 반환한다. 이벤트가 처리되는 요소 정보를 얻을 수 있다.
    • e.clientX, e.clientY : 포인터 관련 이벤트에서, 커서의 상대좌표(브라우저 화면 기준)

    📒 Event Bubbling, Capturing

    1. 이벤트 버블링(Bubbling)

    이벤트 버블링(Bubbling)은, 하위요소에서 상위요소로 이벤트 전파방식을 말한다.

    이벤트 핸들러가 할당된 요소에 이벤트가 발생하면, 이것이 동작한 뒤 이어서 부모 요소의 핸들러가 순차적으로 동작하는 것이다.

    위에서 언급했듯 이벤트 바인딩의 기본값이며, 거의 모든 이벤트는 버블링이 된다고 생각하면 된다. (예외 : focus)

     

    * 버블링 중단하기 : stopPropagation()

     

    위처럼, 대부분의 이벤트는 기본적으로 발생DOM 으로부터, window 객체까지 거슬러 올라가며 이벤트 핸들러가 호출된다.

    이같은 버블링에 따른 의도치 않은 핸들러 호출을 방지하는 방법이 있다.

    이벤트 핸들러 함수에 e.stopPropagation() 을 추가하면, 상위요소로 이벤트 전달을 방지할 수 있다.

    <div onClick={() => alert('div')}>
      <button onClick={(e) => {
        e.stopPropagation();
        alert('button');
      }}>
      	Click me!
      </button>
    </div>

    * 꼭 필요한 경우를 제외하곤 버블링을 막지 않는것을 권장한다.

     

    2. 이벤트 캡쳐링(Capturing)

    이벤트 캡쳐링(Capturing)은 상위요소에서 하위요소로 전파하는 방식이다. 버블링처럼 자주 쓰이진 않으나, 종종 유용한 전파방식이다. 

    이벤트가 발생하면, 해당 DOM이 아닌 상위요소(window) 부터 해당 DOM으로 내려오면서 핸들러를 실행한다.

     

    3. 이벤트 위임(Delegation)

    앞서 언급한 이벤트 버블링을 활용하여, 상위요소에서 하위요소의 이벤트를 제어하는 패턴을 "이벤트 위임" 이라고 한다.

    <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');
      });
    });

    예시는, querySelectorAll() 을 통해 모든 체크박스에 alert 핸들러를 추가한 코드이다.

    여기서, <li> 및 체크박스를 추가한다고 가정하자. 그러면, 새로 추가한 체크박스는 핸들러가 적용되지 않을 것이다.

    (querySelectorAll() 은 이미 2개의 리스트만을 담고 있으므로)

     

    // 새 리스트 아이템을 추가하는 코드
    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);

     

    그래서, 각 <li> 태그가 아닌, 상위 <ul> 태그인 "itemList"에 클릭 이벤트를 위임한다.

    그리하면, 리스트가 추가되어도 그때마다 핸들러를 추가해줄 작업소요가 줄어들게되는 것이다.

    // var inputs = document.querySelectorAll('input');
    // inputs.forEach(function(input) {
    // 	input.addEventListener('click', function() {
    // 		alert('clicked');
    // 	});
    // });
    
    // Event Delegation
    var itemList = document.querySelector('.itemList');
    itemList.addEventListener('click', function(event) {
    	alert('clicked');
    });

    순서가 반대가 되긴 했지만, React 이벤트를 공부하면서 Javascript의 기본 이벤트 개념과 원리에 대해서 공부할 필요성을 느꼈다.

    핸들링, 바인딩, 버블링 과 같은 기본개념을 선정리하는 목적으로 포스팅했으나, 추후 이벤트 심화내용도 보강하도록 하겠다!

     

    [출처]

    - ko.javascript : ko.javascript.info/bubbling-and-capturing

    - 캡틴판교 님의 블로그 : joshua1988.github.io/web-development/javascript/event-propagation-delegation/

    - victorydntmd 님의 블로그 : victorydntmd.tistory.com/85

    반응형
Designed by Tistory.