JS - Event

Leo·2021년 3월 27일
0

JavaScript

목록 보기
2/3
post-thumbnail

Event

이벤트(Event)는 말 그대로 어떤 사건을 의미한다. 브라우저는 이벤트를 감지할 수 있는데 이를 통해서 사용자와 웹페이지가 서로 상호작용할 수 있다.

이벤트 등록

특정한 이벤트가 발생했을 때 이를 처리해주는 함수를 보통 이벤트 리스너(Event Listener), 이벤트 핸들러(Event Handler)라고 부른다. 이벤트를 등록하는 방법은 3가지가 있다.

inline 방식

인라인(inline) 방식은 HTML 요소의 속성에 이벤트를 지정하는 방식이다. HTML과 JavaScript를 혼용하기 때문에 잘 사용하지 않는다.

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <button onclick="clickHandler()">Click</button>
  <script>
    function clickHandler() {
      console.log("Clicked");
    }
  </script>
</body>
</html>

property 방식

프로퍼티(property) 방식은 JavaScript 코드에서 프로퍼티로 등록해서 사용하는 방식이다. HTML과 JavaScript가 혼용되지 않고 하나의 이벤트씩만 바인딩이 가능하다.

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <button class="btn">Click</button>
  <script>
    const btn = document.querySelector(".btn");
    btn.onclick = function() {
      alert("Clicked");
    }
  </script>
</body>
</html>

자주 사용하는 이벤트

  • onblur: 객체가 focus를 잃었을 때
  • onchange: 객체의 내용이 바뀌고 focus를 잃었을 때
  • onclick: 객체를 클릭했을 때
  • ondblclick: 더블클릭할 때
  • onerror: 에러가 발생했을 때
  • onfocus: 객체에 focus가 되었을 때
  • onkeydown: 키를 눌렀을 때
  • onkeypress: 키를 누르고 있을 때
  • onkeyup: 키를 눌렀다 뗐을 때
  • onload: 문서나 객체가 로딩되었을 때
  • onmouseover: 마우스가 객체 위에 올라왔을 때
  • onmouseout: 마우스가 객체 바깥으로 나갔을 때
  • onreset: Reset 버튼을 눌렀을 때
  • onresize: 객체의 크기가 바뀌었을 때
  • onscroll: 스크롤바를 조작할 때
  • onsubmit: 폼이 전송될 때

addEventlistener() 방식

addEventlistener() 방식은 한 개이상의 이벤트를 바인딩할 수 있고 이벤트 버블링과 이벤트 캡쳐링을 지원한다. 위의 이벤트명에서 on을 제외하고 사용하며 가장 많이 사용되는 방식이다.

<!DOCTYPE html>
<html lang="en">
<head>...</head>
<body>
  <button class="btn">Click</button>
  <script>
    const btn = document.querySelector(".btn");

    function clickHandler() {
      console.log("Clicked");
    }
    
    btn.addEventListener("click", clickHandler);
  </script>
</body>
</html>

이벤트 버블링

이벤트 버블링(Event Bubbling)은 화면의 특정 요소에서 이벤트가 발생했을 때 해당 이벤트가 더 상위 요소들로 전달되어 가는 특성을 의미한다.

<body>
  <body>
  <div class="outer">
   outer 
    <div class="middle">
     middle 
      <div class="inner">
        inner
      </div>
    </div>
  </div>
(function() {
  const divs = document.querySelectorAll("div");
  divs.forEach(function(div) {
    div.addEventListener("click", handler, {
      capture: false,
    });
  })

  function handler(e) {
    console.log(e.currentTarget.className);
  }
})();

outer, middle, inner에 이벤트를 등록하고 addEventListener의 3번 째 인자로써 옵션 객체를 capture: false를 넘겨주면 이벤트가 하위 요소에서 상위 요소로 전파되는 이벤트 버블링 방식으로 동작한다. 즉, inner를 클릭하면 콘솔에 다음과 같이 inner부터 차례대로 출력되는 것을 확인할 수 있다.

참고로 addEventListener의 3번 째 인자를 생략할 경우에는 false를 기본 값으로 동작한다.

이벤트 캡쳐링

이벤트 캡쳐링(Event capturing)은 이벤트 버블링과는 반대로 전파되는 방식을 의미한다.

<body>
  <body>
  <div class="outer">
   outer 
    <div class="middle">
     middle 
      <div class="inner">
        inner
      </div>
    </div>
  </div>
(function() {
  const divs = document.querySelectorAll("div");
  divs.forEach(function(div) {
    div.addEventListener("click", handler, {
      capture: true,	
    });
  })

  function handler(e) {
    console.log(e.currentTarget.className);
  }
})();

addEventListener의 3번 째 인자로써 옵션 객체를 capture: true로 변경한 다음 inner를 클릭하면 이벤트 버블링의 전파방식과는 반대로 출력되는 것을 확인할 수 있다.

event.stopPropagation()

event.stopPropagation API는 이벤트가 전파되는 것을 막는다. 따라서 이벤트 버블링의 경우에는 클릭한 요소의 이벤트만 발생시키고 상위 요소로 이벤트를 전달하는 것을 막는 역할을 한다. 그리고 이벤트 캡쳐의 경우에는 클릭한 요소의 최상위 요소의 이벤트만 동작시키고 하위 요소들로 이벤트를 전달하지 않는다.

(function() {
  const divs = document.querySelectorAll("div");
  divs.forEach(function(div) {
    div.addEventListener("click", handler, {
      capture: false,	// 이벤트 버블링
    });
  })

  function handler(e) {
    e.stopPropagation();
    console.log(e.currentTarget.className);	// inner만 출력
  }
})();
(function() {
  const divs = document.querySelectorAll("div");
  divs.forEach(function(div) {
    div.addEventListener("click", handler, {
      capture: true,	// 이벤트 캡쳐링
    });
  })

  function handler(e) {
    e.stopPropagation();
    console.log(e.currentTarget.className);	// outer만 출력
  }
})();

이벤트 위임

이벤트 위임(Event Delegation)은 각각의 하위 요소에 이벤트를 등록하지 않고 상위 요소에만 등록해 하위 요소들의 이벤트를 제어하는 방식이다. 이벤트를 사용함에 있어서 성능과 유지보수를 위해 필수적으로 사용해야할 코딩 패턴이다.

<body>
  <div class="container">
    <div class="btn btn1" data-value="1">
      <img class="icon" src="sample-1.png" alt="Neo">
      <span class="icon-label">Neo</span>
    </div>
    <div class="btn btn2" data-value="2">
      <img class="icon" src="sample-2.png" alt="Ryan">
      <span class="icon-label">Ryan</span>
    </div>
  </div>
</body>

위의 예시를 보면 container안에 Neo와 Ryan이라는 2개의 버튼이 존재하고 이들은 각각 imgspan 태그를 포함하고 있다. 전체 요소인 container에 이벤트 위임을 사용할 경우에 버튼의 imgspan 그리고 회색 배경을 클릭하더라도 동일하게 data-value의 값을 출력 하기 위해서는 어떻게 해야할까? 여기에는 2가지 방법이 있다.

css의 pointer-events 속성으로 처리

CSS에서 제공하는 속성인 pointer-events를 사용하는 방법이다. 특정 요소에 이 속성의 값을 none으로 지정하면 해당 속성의 이벤트가 동작하지 않을 뿐만 아니라 특정 요소가 포함하고 있는 내부의 요소들의 이벤트까지 동작하지 않는다.

/* CSS */
.btn .icon {  pointer-events: none; }
.btn .icon-label { pointer-events: none; }
/* JS */
const container = document.querySelector(".container");

function clickHandler(e) {
	console.log(e.target.dataset.value);
}

container.addEventListener("click", clickHandler);

JavaScript만으로 처리

JavaScript의 내부 로직을 통해서 특정 조건에서만 이벤트가 동작하도록 하는 방법이다. 주로 이 방식을 사용해서 이벤트 위임을 처리한다.

const container = document.querySelector(".container");

function clickHandler(e) {
  let element = e.target;
  while (!element.classList.contains("btn")) {
    element = element.parentNode;

    if (element.nodeName === "BODY") {
      element = null;
      return
    }
  };
  console.log(element.dataset.value);
}

container.addEventListener("click", clickHandler);

참조


0개의 댓글