ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JavaScript | Event Handling 이벤트 처리
    JavaScript/JavaScript 2020. 4. 9. 12:38

    Evnent Handler 이벤트 처리기 등록 방법

    이벤트 주도형 프로그램(event driven program)에서는 이벤트가 발생했을 때 실행할 함수를 등록해 두는데
    이때 실행되는 함수를 이벤트 처리기 또는 이벤트 리스너라고 함

    1. HTML 요소의 이벤트 처리기 속성에 설정
      HTML 문서를 읽어들일 때 이벤트 처리기도 함께 설정하기 때문에 설정하기 쉬움

      <input type="button" onclick="changeColor();"/>
    2. DOM 요소 객체의 이벤트 처리기 프로퍼티에 설정
      HTML과 자바스크립트를 분리해서 작성 가능
      → 겸손한 자바스크립트 구현 → 프로그램 유지 보수성 향상

      const btn = document.getElementById('button');
      btn.onclick = changeColor();
    3. addEventListener 메서드 사용

      const btn = document.getElementById('button');
      btn.addEventListener('click', changeColor, false );

    이벤트 처리기의 문제점

    HTML 요소의 이벤트 처리기 속성에 설정

    • HTML과 자바스크립트 프로그램이 뒤섞여 프로그램 가독성이 떨어짐
      결과적으로 프로그램 유지 보수성이 떨어짐
    • 특정 요소의 특정 이벤트에 단 하나의 이벤트 처리기만 등록 가능
      같은 요소에 똑같은 이벤트를 처리하는 이벤트 처리기를 등록하면 나중에 등록한 함수가 이전 함수를 덮어씀

    DOM 요소 객체의 이벤트 처리기 프로퍼티에 설정

    • 특정 요소의 특정 이벤트에 대해 단 하나의 이벤트 처리기만 등록 가능
      HTML에 인라인으로 작성하는 것과 동일한 단점

    addEventListener 메서드로 이벤트 리스너 등록

    DOM Level 2부터 추가되었으며 위에서 언급한 이벤트 처리기의 문제점 해결

    • addEventListener로 등록한 함수는 이벤트 리스너라고 함
      그 외 방법으로 등록한 함수는 이벤트 처리기라고 함
    • 같은 요소의 같은 이벤트에 여러 개의 이벤트 리스너 등록 가능

    형식

    target.addEventListener(type, listener, useCapture);
    • target: 이벤트 리스너를 등록할 DOM 노드
    • type: 이벤트 유형을 뜻하는 문자열 ex) "click", "mouseup" etc.
    • listener: 이벤트가 발생했을 때 처리를 담당하는 콜백 함수의 참조
    • useCapture: 이벤트 단계, 캡처링/버블링(default) 단계 설정 가능

    예제

    div#box 요소를 클릭하면 div 의 배경색을 빨간색으로 바꾸는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
                const el = document.getElementById('box');
    
                el.addEventListener('click', event => {
                    event.currentTarget.style.backgroundColor = 'red';
                }, false)
            }
            /*
            window.onload = () => {
                const el = document.getElementById('box');
    
                const changeColor = (e) => {
                    e.currentTarget.style.background = "red";
                }
                el.addEventListener('click', changeColor);
            }
            */
        </script>
      </head>
      <body>
          <div id="box">
              click me
          </div>
      </body>
    </html>
    • 이벤트 리스너인 익명 함수가 받는 event 인자는 이벤트 객체
      이벤트 객체에는 발생한 이벤트의 다양한 정보가 들어있음
    • event.currentTarget은 이벤트가 발생한 요소를 참조하는 요소 객체
    • 이벤트 처리기를 등록할 때와는 다르게 'onclick'에서 on을 뺀 'click'만 작성
    • false는 이벤트의 버블링 단계에서 이벤트를 캡처(발생)하라는 뜻
      생략시 false가 기본값으로 적용되며 일반적으로 false를 많이 사용
    • 주석처럼 이벤트 리스너로 사용할 함수를 먼저 선언한 후 등록 하는 것도 가능

    addEventListener 메서드 장점

    • 같은 요소의 같은 이벤트에 여러 개의 이벤트 리스너 등록 가능
    • 버블링 단계와 캡처링 단계에서 활용 가능
      이벤트 처리기는 버블링 단계의 이벤트만 캡처 가능
    • removeEventListener, stopPropagation, preventDefault 등의 메서드로 이벤트 전파를 정밀하게 제어 가능
    • HTML 요소를 포함한 모든 DOM 노드에 이벤트 리스너 등록 가능

    removeEventListener 메서드로 이벤트 리스너 삭제

    target.removeEventListener(type, listener, useCapture);
    • removeEventListener로 이미 등록한 이벤트 리스너 중 동일한 인자를 받는 이벤트 리스너를 삭제함
    • removeEventListener 의 매개변수는 addEventListener와 동일
    • 등록한 이벤트 리스너가 익명함수라면 기본적으로는 삭제 불가
      but, 이벤트 리스너 안에서 arguments.callee 를 사용하면 삭제 가능
      ※ ES6 화살표 함수에서는 사용 불가

    예제

    이벤트 리스너를 등록하면서 리스너 안에서 이벤트 리스너를 삭제하도록 하는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
          window.onload = () => {
            const el = document.getElementById("btn");
            
            el.addEventListener("click", function (e) {
              console.log("hi");
              el.removeEventListener("click", arguments.callee);
            });
            
          };
        </script>
      </head>
      <body>
        <button id="btn">
          click me
        </button>
      </body>
    </html>

    Event 객체

    이벤트 처리기와 이벤트 리스너는 이벤트 객체를 인자로 전달받음
    이벤트 객체는 해당 이벤트의 다양한 정보를 저장한 프로퍼티와 이벤트 흐름을 제어하는 메서드를 가지고 있음

    const changeColor = (event) => {
      event.currentTarget.style.background = "red";
    };
    • 위에서 작성했던 changeColor 함수가 받는 인자가 event 객체
    • 함수의 지역 변수로 사용되기 때문에 이름은 event 외에도 사용 가능
      but, 이벤트 객체임을 명시하기 위해 event나 간단히 e를 사용하는 것이 관례
    • 이벤트 객체가 갖는 프로퍼티는 발생 이벤트 유형에 따라 다름

    Event 객체_공통 Property

    이벤트 객체의 프로퍼티는 이벤트 유형에 따라 다르나 공통으로 갖는 프로퍼티들도 존재

    프로퍼티 값 타입 설명
    type 문자열 이벤트 이름('click', 'mousemove' etc.)
    target 요소 객체 이벤트가 발생한 요소
    currentTarget 요소 객체 처리를 담당하는 이벤트 리스너가 등록된 요소 객체
    eventPhase 정수 이벤트 전파 단계(1: 캡처링, 2: 타겟, 3:버블링)
    timeStamp 정수 이벤트 발생 시각(1970/1/1.00:00:00부터 경과한 밀리초)
    bubbles 논리값 버블링 단계인지를 뜻하는 값
    cancelable 논리값 preventDefault()로 기본 이벤트를 취소할 수 있는지 여부 표현
    defaultPrevented 논리값 preventDefault()로 기본 작업이 취소되었는지를 나타냄
    isTrusted 논리값 해당 이벤트가 사용자의 액션에 의해 생성되었는지를 뜻함

    Mouse Event 객체

    대표적인 마우스 이벤트
    click, dblclick, mousedown, mouseup, mousemove, mouseover, mouseout etc.

    프로퍼티 값 타입 설명
    screenX, screenY 정수 클릭한 위치의 화면 좌표(컴퓨터 화면의 좌상단 꼭짓점이 원점)
    clientX, clientY 정수 클릭한 위치의 윈도우 좌표(표시 영역의 좌상단 꼭짓점이 원점)
    pageX, pageY 정수 클릭한 위치의 문서 좌표(문서의 왼쪽 위 꼭짓점이 원점)
    offsetX, offsetY 정수 이벤트가 발생한 요소의 상대 좌표(요소의 왼쪽 위 꼭짓점이 원점)
    altKey 논리값 Alt가 눌렸는지를 뜻함
    ctrlKey 논리값 Ctrl이 눌렸는지를 뜻함
    shiftKey 논리값 Shift가 눌렸는지를 뜻함
    detail 정수 이벤트의 자세한 정보: 마우스 이벤트의 경우 클릭 횟수
    button 정수 눌린 마우스의 버튼(0: 왼쪽 버튼, 2: 휠 버튼, 3: 오른쪽 버튼)
    relatedTarget 객체 mouseover 이벤트에서는 마우스가 떠난 노드
    mouseout 이벤트에서는 마우스가 들어온 노드

    예제

    div 요소를 마우스로 드래그할 수 있도록 구현한 예제
    기본 HTML 구조에 javascript로만 작성

    // 요소 생성후 body에 추가
    const box = document.createElement("div");
    const text = document.createTextNode("JavaScript");
    box.appendChild(text);
    document.body.appendChild(box);
    
    // box 노드 스타일링
    box.style.width = "150px";
    /*
    다른 스타일링은 중요하지 않지만
    position을 absolute로 설정해야
    브라우저 왼쪽 위 꼭짓점을 기준으로 박스의 위치를
    조작할 수 있음
    */
    box.style.position = "absolute";
    box.style.padding = "10px";
    box.style.background = "red";
    box.style.textAlign = "center";
    box.style.color = "white";
    box.style.cursor = "pointer";
    
    // box의 왼쪽 위 꼭짓점을 기준으로
    // box 내부의 클릭 위치를 상대적으로 저장할 변수
    let boxOffsetX, boxOffsetY;
    
    // mousemove에 연결할 이벤트 리스너
    // box의 위치를 이벤트 객체의 pageX, pageY로 조작
    const moveListener = (e) => {
      box.style.top = `${e.pageY - boxOffsetY}px`;
      box.style.left = `${e.pageX - boxOffsetX}px`;
    };
    
    // box에 mousedown 이벤트가 발생하는 순간
    // boxOffsetX, Y를 저장하고
    // document 객체의 mousemove 이벤트에 moveListener 등록 
    box.addEventListener("mousedown", (e) => {
      boxOffsetX = e.offsetX;
      boxOffsetY = e.offsetY;
      document.addEventListener("mousemove", moveListener);
    });
    
    // document 객체에서 마우스가 떨어지는 순간
    // document 객체의 mousemove 이벤트에 등록된 리스너 제거
    document.addEventListener("mouseup", (e) => {
      document.removeEventListener("mousemove", moveListener);
    });
    

    ※ box 요소가 아닌 document 객체에 리스너를 연결하는 이유 ?
    책에 설명되어 있지 않아서 정확히는 모르겠으나 box에 연결해도 작동은 함
    box의 범위가 좁아서 그런지 작은 차이로도 마우스 이벤트가 해제되거나
    의도대로 움직이지 않는 경우가 발생해서 document에 리스너를 연결한 것으로 예상


    Keyboard Event 객체

    대표적인 키보드 이벤트
    keydown, keypress, keyup etc.

    프로퍼티 값 타입 설명
    altKey 논리값 Alt가 눌렸는지를 뜻하는 논리값
    ctrlKey 논리값 Ctrl이 눌렸는지를 뜻하는 논리값
    shiftKey 논리값 Shift가 눌렸는지를 뜻하는 논리값
    metaKey 논리값 Meta 키가 눌렸는지를 뜻하는 논리값
    맥은 Command, 윈도우는 window
    key 문자열 눌린 키의 DOMString
    keyCode 정수 눌린 키의 키 코드
    code 문자열 눌린 키가 키보드에서 차지하는 물리적 위치를 뜻하는 문자열

    예제

    키보드 이벤트가 발생했을 때 해당 이벤트의 프로퍼티를 출력하는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
          window.onload = () => {
            const display = document.getElementById("display");
            let s = "";
    
            const showKey = (e) => {
              const prop = [
                "altKey",
                "ctrlKey",
                "shiftKey",
                "metaKey",
                "key",
                "code",
                "keyCode",
              ];
    
              prop.forEach((v) => {
                s += `<br>${v}: ${e[v]}`;
              });
    
              s += `-> ${String.fromCharCode(e.keyCode)}`;
              display.innerHTML = s;
              
            };
            
            document.addEventListener("keydown", showKey, false);
          };
        </script>
      </head>
      <body>
        <p id="display"></p>
      </body>
    </html>
    
    <!--
    ___Shift + a 입력 결과___
    altKey: false
    ctrlKey: false
    shiftKey: true
    metaKey: false
    key: A
    code: KeyA
    keyCode: 65-> A
    -->

    ※ String.fromCharCode()
    UTF-16 코드 유닛의 시퀀스로부터 문자열을 생성해 반환
    출처: [MDN_https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode]


    Event Propagation 이벤트 전파

    Window, XMLHttpRequest 객체처럼 단독으로 존재하는 객체에서 이벤트가 발생하면
    웹 브라우저는 해당 객체에 등록된 이벤트 처리기를 호출

    그러나 이벤트가 HTML요소에서 발생하면 그 요소를 포함한 그 요소의 모든 조상 요소에 이벤트를 전파

    예제

    'click' 이벤트의 전파를 확인하는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
          window.onload = () => {
              const inner1 = document.getElementById("inner1");
               const outer = document.getElementById("outer");
    
              inner1.addEventListener("click", () => {
                console.log("inner1 Event");
              });
               outer.addEventListener("click", () => {
                console.log("outer Event");
               });
              window.addEventListener("click", () => {
                console.log("window Event");
              });
          };
        </script>
      </head>
      <body>
        <div id="outer">
          <div id="inner1">inner1</div>
        </div>
      </body>
    </html>
    
    <!--
    ___브라우저의 빈 화면 클릭 시___
    window Event
    
    ___inner1 div 클릭 시___
    inner1 Event
    outer Event
    window Event
    
    ___outer div 클릭 시___
    outer Event
    window Event
    -->

    ※ 'click' 이벤트가 발생하면 'click' 이벤트가 전파되는 것으로 이벤트와 이벤트 리스너를 구분해서 생각하면 이해하기 수월함


    이벤트 단계

    HTML 에서는 부모 요소의 보더 박스 안에 자식 요소를 배치하며 자식 요소는 부모 요소 안에 겹쳐진 상태로 표시됨
    이런 상태에서 자식 요소를 클릭하면 컴퓨터는 자식 요소를 클릭했는지 부모 요소를 클릭했는지를 알아낼 방법이 없음
    그래서 요소에서 이벤트가 발생하면 DOM 트리 관련 요소 전체에
    해당 이벤트에 반응하는 이벤트 처리기나 이벤트 리스너가 등록되어 있는지를 확인하는 작업을 거침

    1. 캡처링 단계

    • 이벤트가 Window 객체에서 출발해서 DOM 트리를 타고 이벤트 타깃까지 전파
    • 이 단계에 반응하도록 등록된 이벤트 리스너는 이벤트가 발생한 요소에 등록된 이벤트 처리기나 이벤트 리스너보다 먼저 실행됨
    • addEventListener 메서드의 useCapture 매개변수에 true를 인자로 전달하면 캡처링 단계에 이벤트 리스너 실행
    • 이벤트 타깃이 이벤트를 수신하기 전에 이벤트를 빼돌리는(캡처하는) 단계라는 뜻에서 캡처링 단계라고 함

    Event Target 이벤트 타깃
    이벤트가 발생한 요소를 이벤트 타깃이라고 함
    이벤트를 발생시키는 것을 이벤트를 발사(Fire)한다고 표현하기도 함


    2. 타깃 단계

    • 이벤트가 이벤트 타깃에 전파되는 단계
    • 이벤트 타깃에 등록된 이벤트 처리기나 이벤트 리스너는 이 시점에 실행
    • 이벤트 처리기의 경우 타깃 단계와 버블링 단계에서만 실행됨

    3. 버블링 단계

    • 이벤트가 이벤트 타깃에서 출발해서 DOM 트리를 타고 Window 객체까지 전파
    • 거품이 올라오는 것처럼 이벤트가 DOM 트리 아래에서부터 위로 올라온다는 뜻에서 버블링 단계라고 함
    • useCapture를 false로 설정하면 버블링 단계에서 이벤트 리스너 실행
    • 같은 요소의 캡처링 단계와 버블링 단계에 각각 이벤트 리스너 등록 가능
    • 같은 요소의 같은 이벤트, 같은 단계에 반응하게 등록된 이벤트 처리기와 리스너가 있다면
      처리기가 먼저 실행되고 리스너가 등록된 순서에 따라 순서대로 실행됨

    ※ focus, blur 이벤트는 해당 요소에만 필요한 이벤트이므로 버블링 발생 X


    예제

    이벤트 단계에 따라 실행되는 이벤트 리스너의 실행 순서를 확인하는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
          window.onload = () => {
              const outer = document.getElementById("outer");
              const inner2 = document.getElementById("inner2");
    
              outer.addEventListener(
                "click",
                (e) => {
                  console.log("outer bubbling");
                },
                false
              );
              outer.addEventListener(
                "click",
                (e) => {
                  console.log("outer capturing");
                },
                true
              );
              inner2.addEventListener(
                "click",
                (e) => {
                  console.log("inner2");
                },
                false
              );
            };
        </script>
      </head>
      <body>
        <div id="outer">
          <div id="inner1">inner1</div>
          <div id="inner2">inner2</div>
        </div>
      </body>
    </html>
    
    <!--
    ___div#inner2 요소 클릭 시___
    outer capturing
    inner2
    outer bubbling
    -->

    stopPropagation 이벤트 전파 취소

    이벤트 리스너 안에서 이벤트 객체의 stopPropagation 메서드를 호출하면 이벤트가 그 다음 요소로 전파되는 것을 차단
    그러나 해당 요소 객체의 그 이벤트에 등록한 다른 이벤트 리스너는 실행됨

    event.stopPropagation();

    예제

    stopPropagation 사용 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              const outer = document.getElementById("outer");
              const inner2 = document.getElementById("inner2");
    
              outer.addEventListener(
                "click",
                (e) => {
                  console.log("outer bubbling");
                },
                false
              );
              outer.addEventListener(
                "click",
                (e) => {
                  console.log("outer capturing");
                },
                true
              );
              inner2.addEventListener(
                "click",
                (e) => {
                  console.log("inner2 (1)");
                  e.stopPropagation();
                },
                false
              );
              inner2.addEventListener(
                "click",
                (e) => {
                  console.log("inner2 (2)");
                },
                false
              );
            };
        </script>
      </head>
      <body>
        <div id="outer">
          <div id="inner1">inner1</div>
          <div id="inner2">inner2</div>
        </div>
      </body>
    </html>
    
    <!--
    ___div#inner2 요소 클릭 시___
    outer capturing
    inner2 (1)
    inner2 (2)
    -->

    ※ div#inner2까지 이벤트가 전파된 뒤 이벤트 전파가 차단 돼서

    div#outer의 버블링 단계에 등록된 이벤트 리스너는 실행 되지 않음


    stopImmediatePropagation 이벤트 전파 일시 정지

    사용법은 stopPropagation과 동일하나
    해당 요소의 같은 이벤트에 등록한 다른 이벤트 리스너도 일시적으로 멈춤
    이벤트 전파가 즉시(Immediate) 멈추기 때문에 같은 요소의 다른 이벤트 리스너에게도 이벤트가 전파되지 않음

     

    event.stopImmediatePropagation();

    예제

    stopImmediatePropagation 사용 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              const outer = document.getElementById("outer");
              const inner2 = document.getElementById("inner2");
    
              outer.addEventListener(
                "click",
                (e) => {
                  console.log("outer bubbling");
                },
                false
              );
              outer.addEventListener(
                "click",
                (e) => {
                  console.log("outer capturing");
                },
                true
              );
              inner2.addEventListener(
                "click",
                (e) => {
                  console.log("inner2 (1)");
                  e.stopImmediatePropagation();
                },
                false
              );
              inner2.addEventListener(
                "click",
                (e) => {
                  console.log("inner2 (2)");
                },
                false
              );
            };
        </script>
      </head>
      <body>
        <div id="outer">
          <div id="inner1">inner1</div>
          <div id="inner2">inner2</div>
        </div>
      </body>
    </html>
    
    <!--
    ___div#inner2 요소 클릭 시___
    outer capturing
    inner2 (1)
    -->

    ※ div#inner2에 첫 번째로 등록된 이벤트 리스너에서 이벤트 전파를 일시 정지(즉시 정지)시켰기 때문에

    해당 요소에 두 번째로 등록된 이벤트 리스너는 실행되지 않음


    preventDefault 기본 동작 취소

    a 요소를 클릭하면 링크된 페이지로 이동하는데,
    이와 같이 웹 브라우저에 구현된 기본 동작을 취소할 때
    preventDefault 메서드를 사용함

    event.preventDefault();
    • 해당 이벤트 객체의 cancelable 프로퍼티가 true이면 기본 동작 취소 가능
    • false인 경우에는 preventDefault 메서드로 기본 동작 취소 불가

    예제

    preventDefault 사용 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              const anchor = document.getElementsByTagName("a");
              anchor[0].addEventListener("click", (e) => {
                if (confirm("페이지를 이동하시겠습니까?")) {
                  e.preventDefault();
                }
              });
            };
        </script>
      </head>
      <body>
        <a href="https://www.naver.com">하이</a>
      </body>
    </html>

    ※ 위의 예제를 실행해보면 확인 버튼을 눌렀을 때는 기본 동작이 차단되어서
    페이지가 이동하지 않지만 반대로 취소 버튼을 누르면 페이지가 이동함


    이벤트 리스너 안의 this

    자바스크립트에서 this는 함수가 호출되어 실행되는 시점에 값이 결정됨
    this의 값은 '함수가 호출되었을 때 함수가 속해 있던 객체의 참조'

    이벤트 리스너 안의 this는 이벤트가 발생한 요소 객체

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              function Person(name) {
                this.name = name;
              }
    
              Person.prototype.sayHello = function () {
                console.log(`Hello! ${this.name}`);
                console.log("this: ", this);
              };
    
              const person = new Person("TOM");
    
              const btn = document.querySelector("#btn");
              btn.addEventListener("click", person.sayHello, false);
            };
        </script>
      </head>
      <body>
        <button id="btn">버튼</button>
      </body>
    </html>
    
    <!-- 
    Hello!
    this:  <button id="btn">버튼</button>
    -->

    ※ 버튼을 클릭하면 person.sayHello가 실행되지만
    콘솔창을 확인해 보면 Hello!만 출력되는 것을 볼 수 있음

    이처럼 person.sayHello 내부의 this는 button 요소 객체를 가리킴


    this가 원하는 객체를 가리키도록 만드는 방법

    1. bind 메서드

    bind 메서드를 사용하면 함수가 실행될 때의 this를 지정할 수 있음

    예제

    위의 예제에 bind 메서드 사용

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
    
            /* 위의 예제와 동일하므로 생략 */
    
            btn.addEventListener("click", person.sayHello.bind(person), false);
            };
        </script>
      </head>
      <body>
        <button id="btn">버튼</button>
      </body>
    </html>
    
    <!-- 
    Hello! TOM
    this:  Person {name: "TOM"}
    -->

    2. 익명 함수 안에서 실행

    이벤트 처리기/리스너로 익명 함수를 넘기고 익명 함수 안에서 메서드를 호출하면
    그 메서드의 this가 메서드를 참조하는 객체를 가리키게 됨

    예제

    위의 예제를 익명함수로 바꿔서 작성

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
    
            /* 위의 예제와 동일하므로 생략 */
    
            btn.addEventListener(
                "click",
                function () {
                  person.sayHello();
                },
                false
              );
        </script>
      </head>
      <body>
        <button id="btn">버튼</button>
      </body>
    </html>
    
    <!-- 
    Hello! TOM
    this:  Person {name: "TOM"}
    -->

    ※ 익명 함수를 addEventListener 메서드에 인자로 전달하면 그 함수 안의 this가 메서드를 참조하는 객체로 고정됨


    3. 화살표 함수 사용

    ES6부터 추가된 화살표 함수의 this는 함수를 초기화하는 시점에 결정됨
    객체 안의 메서드를 화살표 함수로 작성하면 그 안의 this가 생성된 객체를 가리키도록 할 수 있음

    예제

    생성자 함수 내부에 화살표 함수로 sayHello 메서드 작성

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              function Person(name) {
                this.name = name;
                this.sayHello = () => {
                  console.log(`Hello! ${this.name}`);
                  console.log("화살표 함수 내부 this:", this);
                };
              }
    
              const person = new Person("TOM");
    
              const btn = document.querySelector("#btn");
              btn.addEventListener("click", person.sayHello, false);
            };
        </script>
      </head>
      <body>
        <button id="btn">버튼</button>
      </body>
    </html>
    
    <!-- 
    Hello! TOM
    화살표 함수 내부 this: Person {name: "TOM", sayHello: ƒ}
    -->

    ※ 단, 화살표 함수로 정의한 함수를 객체의 prototype의 메서드로 추가하면

    의도한 대로 동작하지 않고 this가 Window 객체를 가리키게 됨


    4. addEventListener의 두 번째 인자로 객체 전달

    addEventListener 메서드의 두 번째 인자는 함수를 받지만 객체도 전달 가능
    해당 객체에 handleEvent 메서드가 있으면 그 메서드를 이벤트 리스너로 등록

    예제

    addEventListener의 두 번째 인자로 객체를 전달하고
    객체 내부의 handleEvent 메서드를 이벤트 리스너로 등록하는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              function Person(name) {
                this.name = name;
              }
              
              Person.prototype.handleEvent = function () {
                console.log(`Hello! ${this.name}`);
                console.log("this: ", this);
              };
    
              const person = new Person("TOM");
    
              const btn = document.querySelector("#btn");
              btn.addEventListener("click", person, false);
            };
        </script>
      </head>
      <body>
        <button id="btn">버튼</button>
      </body>
    </html>
    
    <!-- 
    Hello! TOM
    this:  Person {name: "TOM"}
    -->

    ※ handleEvent 메서드의 this는 생성된 객체에 고정되므로 위와 같은 결과가 나옴


    이벤트 리스너에 추가적인 정보 전달하기

    1. 익명 함수 안에서 실행하기

    이벤트 리스너는 이벤트 객체를 인자로 받기 때문에 추가적인 정보를
    전달하기 위해서는 익명 함수를 이벤트 리스너로 지정하고 이벤트 리스너 안에서 함수를 실행해야 함

    예제

    div#div클릭 시 배경색을 빨간색으로 바꾸는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              const div = document.getElementById("div");
    
              div.addEventListener(
                "click",
                (e) => {
                  changeColor(e, "red");
                },
                false
              );
    
              const changeColor = (e, color) => {
                e.currentTarget.style.background = color;
              };
            };
        </script>
      </head>
      <body>
        <div id="div">click me</div>
      </body>
    </html>

    ※ 익명 함수의 인자로는 event 객체를 전달하고,
    실제로 실행할 함수에 event 객체와 더불어 추가적인 정보를 전달


    2. 함수를 반환하는 함수를 이벤트 리스너로 등록하기

    추가적인 정보를 받는 함수를 정의하고 그 함수 내부에서
    이벤트 객체를 인자로 받는 함수를 반환해서 반환된 함수를 이벤트 리스너로 등록하는 방법

    예제

    div#div클릭 시 배경색을 파란색으로 바꾸는 예제

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script>
            window.onload = () => {
              const div = document.getElementById("div");
    
              const changeColor = (color) => {
                return (e) => {
                  e.currentTarget.style.background = color;
                };
              };
    
              div.addEventListener("click", changeColor("blue"), false);
            };
        </script>
      </head>
      <body>
        <div id="div">click me</div>
      </body>
    </html>

    ※ 앞에서 했던 예제와 동일한 동작을 하는 코드지만 함수를 정의하는 부분과

    addEventListener의 인자가 완전히 분리되어서 코드의 가독성이 높아짐


    이벤트에 대해 한 번은 제대로 짚고 넘어가는 것이 좋을 것 같아서 정리해 보았습니다.

    잘못된 내용이나 동작하지 않는 예제를 알려주시면 감사히 반영하겠습니다.

    커스텀 이벤트는 일반적으로 사용하는 이벤트와는 사용 목적이 다른 것 같아서 다음에 따로 다룰 예정입니다.

     

    참고 도서: [모던 자바스크립트 입문/ 저자: 이소 히로시/ 출판사: 길벗]

    댓글

Designed by Tistory.