본문 바로가기
개발 관련/javascript

13. 이벤트

by lazysnack 2022. 7. 13.

다루는 내용

  1. 이벤트 흐름에 대한 이해
  2. 이벤트 핸들러 다루기
  3. 여러 가지 타입의 이벤트
  4. 메모리와 성능

1. 이벤트 흐름에 대한 이해

  • 자바스크립트와 HTML 의 상호작용은 문서나 브라우저 창에서 특정 순간에 일어난 일을 가리키는 이벤트에 의해 처리됨
  • 이벤트는 리스너(핸들러) 로 추적하며 리스너는 이벤트가 일어날 때만 실행됨

1.1 이벤트 버블링

  • 익스플로러의 이벤트 흐름

  • 문서트리에서 가장 깊이 위차한 요소에서 시작해 거슬러 올라가는 방식

    <html>
      <head>
        <title>title</title>
      </head>
      <body>
        <div id="myDiv">click me</div>
      </body>
    </html>
    • <div> 요소를 클릭하면

      1. <div>
      2. <body>
      3. <html>
      4. document

      의 순서대로 발생

1.2 이벤트 캡처링

  • 이벤트 버블링과는 반대로 최상위 노드에서 시작
  • 오래된 브라우저에서는 지원하지 않으므로 이벤트 버블링을 주로 사용함

1.3 DOM 이벤트 흐름

  • 이벤트 캡처링 단계, 타깃 단계, 이벤트 버블링 단계 3가지가 있음
  • 이벤트 캡처링, 이벤트 버블링 두 단계가 다 타깃 단계와 맞물리는 곳이 있으므로 결과적으로 타깃에서 이벤트를 작업할 기회가 두 번 생김 (??)

2. 이벤트 핸들러

  • 사용자 또는 브라우저가 취하는 특정 동작
  • on 으로 시작함
    • Ex) onclike, onload 등

2.1 HTML 이벤트 핸들러

  • 이벤트 핸들러 이름을 HTML 속성에 사용하여 할당할 수 있음

    <input type="button" value="click1" onclick="alert('clicked')"/>
    
    or
    
    <script type="text/javascript">
    function showMessage() {
      alert("hello!");
    }
    </script>
    <input type="buttion" value="click1" onclick="showMessage()"/>
  • 스코프 체인이 확장되어 함수 내부에서 document 와 해당 요소의 맴버에 마치 로컬 변수처럼 접근이 가능(with 를 통해 이루어짐)

    <form method="post">
        <input type="text" name="username" value="">
      <input type="button" value="echo" onclick="alert(username.value)">
    </form>
  • 이벤트 핸들러 코드가 준비되기 전에 HTML 요소가 먼저 화면에 발생하고 사용자가 이를 조작할 경우 에러가 발생할 가능성이 있음

  • 이벤트 핸들러 함수의 스코프 체인 확장 결과가 브라우저마다 다름

  • HTML 과 자바스크립트 간의 커플링이 심하게 될 수가 있음 (변경이 어려움)

2.2 DOM 레벨 0 이벤트 핸들러

  • 전통적으로는 이벤트 핸들러 프로퍼티를 함수에 할당하는 방법이 있음

    var btn = document.getElementById("myBtn");
    btn.onclick = function() {
        alert(this.id); // myBtn
    };
    
    // 이벤트 핸들러를 제거할 경우
    btn.onclick = null;

2.3 DOM 레벨 2 이벤트 핸들러

  • addEventListener(), removeEventListener()

  • 매개변수 (이벤트 이름, 이벤트 핸들러 함수, 이벤트 핸들러를 캡처 단계에서 호출여부)

    var btn = document.getElementById("myBtn");
    btn.addEventListener("click", function() {
      alert(this.id);
    }, false);
  • 위의 예제에서 함수는 익명함수 이기 때문에 removeEventListener 로 지울 수 없음. 따라서 해당 함수를 추가하고 지우고 하려면

    var btn = document.getElementById("myBtn");
    var handler = function() {
      alert(this.id);
    };
    btn.addEventListener("click", handler, false);
    
    //do something
    
    btn.removeEventListener("click", handler, false);

2.4 인터넷 익스플로러 이벤트 핸들러

  • IE8 및 이전 버전에서 사용
  • attachEvent(), detachEvent()

3. Event 객체 및 타입

  • DOM 과 관련된 이벤트가 발생하면 관련 정보는 모두 event 객체에 저장
  • 이벤트 핸들러 내부에서 this 객체는 항상 currentTarget 의 값과 일치하며 target에는 이벤트의 실제 타깃만 포함
  • preventDefault() 메소드는 이벤트의 기본 동작을 취소 (cancelable 이 true)
  • stopPropagation() 메소드는 이벤트 흐름을 멈춰서 이벤트 캡처링이나 버블링을 모두 취소
  • event 객체는 이벤트 핸들러가 아직 실행 중일 때만 존재하며 이벤트 핸들러가 실행을 마치면 event 객체는 파괴됨

3.1 이벤트 타입

  • UI 이벤트는 일반적인 브라우저 이벤트이며, BOM 과 상호작용이 포함될 수 있음
  • 포커스 이벤트는 요소가 포커스를 얻거나 잃을 때
  • 마우스 이벤트는 마우스로 어떤 동작을 취할 때
  • 등의 카테고리가 있으며, 이 외에도 HTML5 에서 정의한 이벤트 집합이 있음

3.2 UI 이벤트

  • 사용자와 직접 연관이 없으며, 하위 호환성을 위해 남겨진 것들
  • load - window 객체의 load 이벤트는 이미지나 자바스크립트 파일, CSS 파일 같은 외부 자원을 포함해 전체 페이지를 완전히 불러왔을 때 발생
  • unload - load 와는 반대로 문서를 완전히 닫을 때 발생하며 일반적으로 다른 페이지로 이동할 때 각종 참조를 제거하여 메모리 누수를 방지하는 목적으로 사용
  • resize - 브라우저 창의 높이나 너비를 바꿀 때 발생하며 브라우저별로 발생 시점에 차이가 있으므로 무거운 코드는 사용 지양

3.3 Focus 이벤트

  • 이벤트 요소가 포커스를 받거나 잃을 때 발생
  • 포커스를 잃는 요소에서 focusout, blur, DOMFocusOut 이 발생
  • 포커스를 얻는 요소에서 focusin, focus, DOMFocusIn 이 발생

3.4 마우스 이벤트와 휠 이벤트

  1. click - 사용자가 주요 마우스 버튼을 클릭하거나 엔터키를 누를 때 발생
  2. dbclick - 사용자가 주요 마우스 버튼을 더블클릭할 때 발생
  3. mousedown - 사용자가 마우스 버튼을 누를 때 발생 (키보드 발생 X)
  4. mouseenter - 마우스 커서가 요소 밖에서 요소 경계 안으로 처음 이동할 때
  5. mouseleaver - 마우스 커서가 요소 위에 있다가 요소 경계 밖으로 이동할 때
  6. mousemove - 마우스 커서가 요소 주변을 이동하는 동안 계속 발생
  7. mouseout - 마우스 커서가 요소 위에 있다가 다른 요소 위로 이동할 때 발생
  8. mouseover - 마우스 커서가 요소 바깥에 있다가 요소 경계 안으로 이동할 때 발생
  9. mouseup - 사용자가 마우스 버튼을 누르고 있다가 놓을 때 발생
  • 해당 이벤트가 지원되는지 확인하려면

    var isSupported = document.implementation.hasFeature("MouseEvents", "2.0");
    
    or 
    
    var isSupported = document.implementation.hasFeature("MouseEvent", "3.0");
  • 뷰포트 기준으로는 clientX/clientY, 페이지 기준으로는 pageX, pageY

  • mousewheel 이벤트는 사용자가 마우스 휠을 세로 방향으로 움직일 때 발생

  • 웹페이지 접근성을 위해(스크린 리더 등 호환)

    1. 코드 실행에는 click
    2. 사용자에게 새 옵션을 제시할 때 onmouseover 사용 금지
    3. 중요한 동작을 dbclick 로 실행 금지

3.5 키보드와 텍스트 이벤트

  1. keydown - 사용자가 키를 처음 누를 때 발생하며, 누르고 있는 동안에 계속 발생
  2. keypress - 사용자가 키를 누른 결과로 문자가 입력되었을 때 처음 발생, 누르고 있는 동안 계속 발생 (textInput 사용 권장)
  3. keyup - 사용자가 키에서 손을 뗄 때 발생

3.6 HTML5 이벤트

  1. contextmenu
    • 마우스 우클릭을 했을 때 나오는 메뉴같은 형태로, 컨텐스트 메뉴가 표시되려는 순간에 발생하므로 개발자가 기본 메뉴를 취소하고 커스터마이징 할 수 있음
    • 마우스 이벤트로 간주되므로 커서 위치와 관련된 프로퍼티를 포함
  2. beforeunload
    • window 에서 발생하며 개발자에게 페이지에서 떠나지 못하게 막을 방법을 제공할 의도로 만들어짐
  3. DOMContentLoaded
    • 항상 load 이벤트보다 먼저 발생하므로 이벤트 핸들러를 등록하거나 다른 DOM 조작을 수행하는 데 쓰임
    • window 의 load 이벤트가 페이지를 완전히 불러와야 발생하므로 외부 자원이 많을 경우 시간이 걸릴 수 있으므로 그럴 경우 DOMContentLoaded 를 등록
  4. readystatechange
    • 문서나 요소를 불러오는 상황에 대한 정보로 readyState 라는 프로퍼티
    • uninitialized, loading, loaded, interactive, complete 라는 순서가 있으나 모든 객체가 이 단계를 순서대로 전부 밟지는 않음

4. 메모리와 성능

  • 자바스크립트에서는 페이지에 존재하는 이벤트 핸들러의 개수가 페이지 성능에 직접적으로 영향을 미치는데
    1. 각 함수가 메모리를 점유하는 객체이기 때문
    2. 이벤트 핸들러를 많이 할당하려면 DOM 접근도 많아지며 이는 전체 페이지의 응답성을 떨어뜨리기 때문

4.1 이벤트 위임

  • 이벤트 핸들러의 갯수에 대해서는 이벤트 핸들러 하나만 할당해서 해당 타입의 이벤트를 모두 처리하는 방법이 있음

    <ul id="myList">
      <li id="go1">go1</li>
        <li id="go2">go2</li>
      <li id="hi">hi</li>
    </ul>
    
    <script>
    var list = document.getElementById("myList");
    var handler = function() {
      switch(this.id) {
        case "go1":
          document.title = "go1";
          break;
    
        case "go2":
          document.title = "go2";
          break;
    
        case "hi":
          document.title = "hi";
          break;
      }
    };
    list.addEventListener("click", handler, false);
    </script>
    • 이벤트가 버블링되어 올라오기 때문에 가능
    • 현실적이기만 하다면 이벤트 타입마다 document 에 이벤트 핸들러 단 하나씩만 등록해서 페이지의 이벤트 전체를 처리하는게 좋음

4.2 이벤트 핸들러 제거

  • 브라우저 코드와 자바스크립트 코드의 연결이 많을 수록 페이지가 느려지는데, 이 문제를 해결하기 위한 다른 방법(하나는 위의 위임 방법)은 더이상 필요하지 않은 이벤트 핸들러를 제거 하는 것

    <div id="myDiv">
      <input type="button" value="click me" id="myBtn">
    </div>
    
    <script>
        var btn = document.getElementById("myBtn");
      btn.onclick = function() {
        // doSomething
    
        btn.onclick = null; // 잔류 핸들러 제거
    
        document.getElementById("myDiv").innerHTML = "진행중";
      };
    </script>
  • 잔류 핸들러가 문제가 되는 다른 상황은 페이지를 떠날 때 이므로, 일반적으로 페이지를 떠나기 전에 onunload 이벤트 핸들러를 사용하여 이벤트 핸들러를 모두 제거하는 편이 좋음

  • onload 에서 한 일은 반드시 onunload에서 취소한다

'개발 관련 > javascript' 카테고리의 다른 글

22. 고급테크닉  (0) 2022.07.13
17. 에러 처리와 디버깅  (0) 2022.07.13
11. DOM 확장  (0) 2022.07.13
10. DOM  (0) 2022.07.13
8. 브라우저 객체 모델  (0) 2022.07.13