ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JS/DOM] 자바스크립트, 문서 객체 모델(Document Object Model) 조작하기(3) - DOM Event
    Frontend 2019. 6. 18. 20:35

    오늘은 DOM에서 이벤트를 처리하는 방법에 대해서 포스팅해보려고 한다.

     

     

    1. HTML 태그 안에서 이벤트 처리기 연결 (Inline Event Handlers)

    이 방법은 이벤트를 발생시킬 HTML 태그 안에 직접 이벤트 핸들러를 추가하는 방법이다. 이 방법을 사용하면 HTML 태그와 자바스크립트 소스가 섞이기 때문에 중간에 이벤트 수정하거나 연결 함수를 바꾸려면 HTML 코드를 수정해야 한다. 

     

    function logMessage (msg) {
      alert(msg || 'logging');
    }
    <div id="listContainer" onclick="logMessage()">
      <ul>
        <li id="list-1">1번</li>
        <li id="list-2">2번</li>
        <li id="list-3">3번</li>
        <li id="list-4">4번</li>
        <li id="list-5">5번</li>
      </ul>
    </div>

     

    태그에 직접 연결해야하기 때문에 이벤트 함수가 전역 공간에 미리 선언되어 있어야 한다. 이 방법은 요즘엔 거의 사용하지 않는 방법이고, HTML 코드와 JS 코드가 뒤섞이기 때문에 사용을 권장하지 않는다. HTML, CSS, JS는 되도록이면 분리해서 사용하는 것이 좋다.

     

     

     

    2. DOM 요소에 이벤트 처리기 연결하기 (Using Element Property)

    이 방법은 이벤트를 발생시킬 요소를 가져온 후 이벤트 핸들러를 연결하는 방법이다. 이벤트 처리기를 HTML 소스에서 실행하는 것이 아니고 이제 스크립트 소스에서 실행한다. 

     

    const $container = document.querySelector('#listContainer');
    
    $container.onclick = logMessage;
    
    function logMessage (msg) {
      alert(msg || 'logging');
    }

     

    위 코드를 실행해보면 event 객체가 자동으로 logMessage 함수의 인자로 넘어오는 것을 확인할 수 있다.

    만약에 event 객체가 아닌 다른 인자를 넣고 싶다면 다음의 방법으로 인자를 전달하면 된다.

     

     

    const $container = document.querySelector('#listContainer');
    
    $container.onclick = function () {
      logMessage('hello');
    };
    
    function logMessage (msg) {
      alert(msg || 'logging');
    }

     

    이 방법은 HTML 태그와 뒤섞이지 않고 이벤트 처리기를 연결할 수 있다는 점에서 이 전의 방법보다 훨씬 낫지만, 하나의 요소에 하나의 이벤트 처리기만 사용할 수 있다는 것이 단점이다.

     

     

     

    3. addEventListener() 함수 사용하기 (Using addEventListener Method)

    addEventListener 함수는 이벤트를 발생시킬 요소에 이벤트 핸들러를 연결해 주는 함수로, 웹 문서에서 이미지텍스트, 엘리먼트 뿐만 아니라 DocumentWindow 객체 등 어디에서든 사용할 수 있다. 또한 이 함수를 이용하면 한 요소에 여러 가지 이벤트를 동시에 연결할 수 있다는 큰 장점이 있다.

     

    const $container = document.querySelector('#listContainer');
    
    $container.addEventListener('click', function () {
      logMessage();
    });
    
    // 또는 $container.addEventListener('click', logMessage);
    
    function logMessage (msg) {
      alert(msg || 'logging');
    }

     

    1. 이벤트 핸들러: 처리할 이벤트 핸들러를 적는다. click, mouseenter, keyup, blur, focus, ... 등등 매우 다양하다.

    2. 함수: function () {}으로 직접 적을 수도 있고 따로 함수를 만들었다면 함수의 이름을 적는다.

    3. 캡쳐링 여부: 기본값은 false이며, false로 지정하면 이벤트 흐름을 버블링만 캐치한다. true로 지정하면 캡쳐링을 캐치한다. 버블링과 캡쳐링에 대해선 아래에서 자세히 다룬다.

     

     

     

    ※ Event handler 안에서 this의 역할 (this in Event Handler)

    See the Pen This in Event by juyeonH (@JY712) on CodePen.

     

    이벤트 처리기 안에서 this는 이벤트에 바인딩된 요소 자체를 가리킨다. 즉, 이벤트 객체의 currentTarget 속성과 동일하다. 그러나 this의 의미는 함수를 어떻게 호출하냐에 따라 값이 변할 수 있으므로 이벤트 처리할 때에는 currentTarget을 대신 사용하는 것이 훨씬 명확하다. 그러므로 이벤트 처리 시 this를 event.currentTarget 속성 대신 사용하는 것은 지양해야 한다.

     

     

     

    ※ event.target vs event.currentTarget

    event 객체에는 target과 currentTarget 속성이 있다.

     

    See the Pen e.target by juyeonH (@JY712) on CodePen.

     

    먼저 위 예제를 보자. 위 예제는 div#listContainer에 event.target을 감지하는 이벤트를 연결하였다. 이제 div 하위 요소들을 클릭해보면 분명 div#listContainer에 이벤트를 걸었음에도 불구하고 클릭하는 요소들이 감지되는 것을 확인할 수 있다. 즉, li 태그나 ul 태그 등등 내가 클릭한 element가 감지된다.

     

     

     

    See the Pen DOM Event by juyeonH (@JY712) on CodePen.

     

    이제 target 대신 currentTarget 속성으로 바꾼 후 이벤트를 실행해보자. 이번엔 내가 어떤 것을 클릭하는지와 상관없이 div#listContainer만 감지된다. 즉, currentTarget은 이벤트에 바인딩된 요소만 가리킨다.

     

     

     

    ※ event.preventDefault();

    이 메소드는 HTML 태그의 기본 동작을 막는 함수이다. 예를 들어서 a 태그는 href 속성에 적은 주소로 페이지를 이동시키는 함수이다. 그런데 때에 따라서는 페이지로 이동시키지 않고 다른 이벤트를 처리하고 싶을 수도 있다. 그럴 때 event.preventDefault()를 사용하면 태그의 기본 동작을 막기 때문에 더 이상 페이지 이동이 일어나지 않는다.

     

    <a href="https://www.naver.com/" target="_blank">NAVER 열기</a>
    document.querySelector('a').addEventListener('click', function (e) {
      e.preventDefault();
    });

     

     

     


    이벤트 전파(Event Propagation): 버블링(Bubbling)과 캡처링(Capturing)

    HTML 요소에 이벤트가 발생하면 그 한 요소에만 이벤트가 일어나는 것이 아니라 연쇄적으로 반응이 일어난다. 이벤트는 상위로 혹은 하위로 전파되는데, 어느 방향으로 전파되느냐에 따라서 버블링(Bubbling)캡처링(Capturing)으로 구분된다. 이벤트가 발생된 해당 요소로부터 부모 요소로 전파되는 것을 버블링이라고 하고, 가장 상위의 부모 요소로부터 이벤트가 발생한 요소로 전파되는 것을 캡처링이라고 한다. 

     

    모든 이벤트는 캡처링으로 시작해서 버블링으로 종료되며, 이러한 이벤트의 흐름을 그림으로 그려보면 다음과 같다.

     

     

     

    addEventListener() 함수에서 세 번째 인자를 true로 지정하면 캡처링으로 전파되는 이벤트를 감지하고 false로 지정하면 버블링으로 전파되는 이벤트를 감지한다.

     

     

    See the Pen capturing by juyeonH (@JY712) on CodePen.

     

    위 예제는 캡처링을 감지하도록 addEventListener() 함수 세 번째 인자로 true를 지정하였다.

    이 경우 li 태그를 클릭하면 상위 요소부터 시작해서 target 요소까지 차례로 출력된다.

     

     

     

     

    See the Pen bubbling by juyeonH (@JY712) on CodePen.

     

    위 예제는 반대로 버블링을 감지하도록 addEventListener() 함수 세 번째 인자로 false를 지정하였다.

    (기본값이 false이므로 아무것도 지정하지 않으면 자동으로 버블링을 감지한다.)

    이 경우 li 태그를 클릭하면 클릭한 target 요소부터 시작해서 상위 요소로 전파되는 것을 확인할 수 있다.

     

     

     

     

    ※ event.stopPropagation();

    만약에 이러한 이벤트 전파를 막고 싶을 경우에는 stopPropagation 함수를 사용할 수 있다. 이 메소드를 사용하면 이벤트가 다음 단계로 전파되지 않는다.

     

    See the Pen bubble_stopPropagation by juyeonH (@JY712) on CodePen.

     

    위 예제에서 1~3번 li 태그에는 stopPropagation() 메소드를 사용했고, 4~5번 태그에는 사용하지 않았다. 이제 1번~3번 li 태그를 클릭하면 더 이상 버블링이 감지되지 않고 오직 타겟 이벤트만 감지하여 이벤트가 발생한다.

     

     

     

     

    See the Pen capture_stopPropagation by juyeonH (@JY712) on CodePen.

     

    위 예제는 캡처링 흐름을 포착하도록 설정한 후, ul 태그에 stopPropagation() 메소드를 사용하였다. 이제 li 태그를 클릭하더라도 ul 태그에서 이벤트 흐름을 차단하였으므로 li 태그의 이벤트가 감지되지 않는다.

     

     

     

     


    이벤트 흐름을 이용한 이벤트 위임(Event Delegation)

    만약에 우리가 5개의 li 태그를 클릭했을 때, 배경색을 오렌지 색으로 변경하는 이벤트를 넣고싶다고 해보자. 이 때 모든 li 태그를 각각 탐색해서 각각 addEventListener() 함수를 사용하여 이벤트를 일일이 등록해주는 것은 너무나도 귀찮은 작업일 것이다. 이 때, 우리는 이벤트 위임(Event Delegation)을 이용할 수 있다.

     

    바로 이벤트가 상위에서 하위로 하위에서 상위로 전파되는 특성을 이용하여 li 태그의 부모 요소에 이벤트를 등록하는 것이다.

     

    See the Pen eventDelegation by juyeonH (@JY712) on CodePen.

     

    ul 태그에 이벤트를 등록한 후, 클릭한 요소의 태그명이나 클래스명, 아이디명을 감지하여 이벤트를 처리한다.

    반응형

    COMMENT