커스텀 이벤트 디스패치(step 21)

KHW·2021년 1월 4일
0

js-study

목록 보기
3/39

커스텀 이벤트 생성

이벤트 객체는 다음과 같이 생성한다.

let event = new Event(type[, options]);

커스텀 이벤트는 핸들러는 addEventListener 로 만 등록할 수 있다

options – 두 개의 선택 프로퍼티가 있는 객체가 옵니다.

  1. bubbles: true/false – true인 경우 이벤트가 버블링 됩니다.
  2. cancelable: true/false – true인 경우 브라우저 '기본 동작’이 실행되지 않습니다.
    ( 기본적으로 {bubbles: false, cancelable: false}.)

ex) 혼자만든 예시

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
    </style>
</head>
<body>
<button id="elem" >자동으로 클릭 되는 버튼</button>

<script>
  let event = new Event("first");			//(1)
  let event2 =  new Event("second");			//(1)
  elem.addEventListener('first',function(){		//(2)
  	alert('1번째 출력');
  })
  elem.addEventListener('second',function(){		//(2)
  	alert('2번째 출력');
  }) 
  elem.dispatchEvent(event);		//(3)
  elem.dispatchEvent(event2);		//(3)


</script>
</body>
</html>

(1) event와 event2라는 변수에 first, second라는 이름의 Event객체를 등록시킨다.
(2) 이벤트리스너를 추가하는데 first라는 이벤트가 오면 alert를 하고 second라는 이벤트가 오면 alert를 실행한다.
(3) elem에 이벤트를 dispatch(전송)한다.
=> (3)의 전송을 받은 elem은 자신의 이벤트들 중에 맞는 이벤트를 찾고 해당 이벤트에 해당하는 함수를 수행한다.

다르게 말하면 만약 (2)와 (3)의 위치가 바뀐다면 alert함수를 수행하지 못한다. 왜냐하면 elem이 전송을 보냈는데 그 시기에 아직 해당하는 이벤트리스너가 추가 되어있지 않기 때문이다.

주의 : new Event로 등록하는 이름과 addEventListener 등록이름이 같아야한다.
new Event('first') <=> addEventListener('first',~~)

커스텀 이벤트 버블링 예시 (아직 이해가 더 필요)

  
<!DOCTYPE html>
<html>

<head>
    <title>TensorFlow.js Tutorial - lemon</title>

</head>

<body>
    <h1 id="elem">Hello from the script!</h1>

<script>
  // 버블링이 일어나면서 document에서 이벤트가 처리됨
 document.addEventListener("hello", function(event) { // (1)
    alert("Hello from " + event.target.tagName); // Hello from H1
  });

  // 이벤트(hello)를 만들고 elem에서 이벤트 디스패치
  let event = new Event("hello", {bubbles: true}); // (2)
  elem.dispatchEvent(event);

  // document에 할당된 핸들러가 동작하고 메시지가 얼럿창에 출력됩니다.

</script>
</body>

</html>

만약 이부분은 {bubbles:true} 부분을 생략한다면 브라우저 로드 시 addEventListener가 발생하지않는다.

기본적으로 이벤트가 발생하면 이벤트 흐름은 document부터 시작해서 해당 위치의 이벤트 발생지점까지 캡쳐링을 하고 다시 버블링을 통해 간다면 {bubbles:true}를 삭제해도 결과가 같을거라 보는데
만약 이런 다른 결과라면 dispatchEvent는 따로 이벤트 흐름의 캡쳐링 없이 말그대로 dispatch(전송) 이므로 document부터의 전송없이 해당 이벤트 위치에 바로 도달하고 그 후 버블링을 통해 처리되지않는것이기 때문일까 개인적으로 생각을 한다. => 이에관련 내용에 의견들을 남겨주면 감사하겠습니다.

new Event vs new CustomEvent

CustomEvent의 Event에 비해 두 번째 인수엔 객체가 들어갈 수 있는데, 개발자는 이 객체에 detail이라는 프로퍼티를 추가해 커스텀 이벤트 관련 정보를 명시하고, 정보를 이벤트에 전달할 수 있습니다.

detail 프로퍼티를 쓰는 이유 : detail이라는 특별한 프로퍼티를 사용하는 이유는 다른 이벤트 프로퍼티와 충돌을 피하기 위해서

토끼예시

  
<!DOCTYPE html>
<html>

<head>
    <title>TensorFlow.js Tutorial - lemon</title>

</head>

<body>
<pre id="rabbit">
  |\   /|
   \|_|/
   /. .\
  =\_Y_/=
   {>o<}
</pre>
<button onclick="hide_function()">hide()를 호출해 토끼 숨기기</button>

<script>
  // hide() will be called automatically in 2 seconds
  function hide_function() {
    let event = new CustomEvent("hide", {
      cancelable: true // cancelable를 true로 설정하지 않으면 preventDefault가 동작하지 않습니다.
    });
    if (!rabbit.dispatchEvent(event)) {
      alert(`기본 동작이 핸들러에 의해 취소되었습니다.`);
    } else {
      rabbit.hidden = true;
    }
  }

  rabbit.addEventListener('hide', function(event) {
    if (confirm("preventDefault를 호출하시겠습니까?")) {
      event.preventDefault();
    }
  });
</script>
</body>

</html>

헷갈림을 방지하기 위해으로 수정하였다.

  1. button을 클릭 할 경우 hide_function()이 일단 실행된다.

  2. event라는 변수에 hide라는 CustomEvent가 등록이 되고 if문을 실행한다.

  3. if문의 rabbit.dispatchEvent(event)가 발생하는 경우 바로 hide라는 것이 addEventListener에 핸들러로 존재하고 있기 때문에 confirm을 실행한다.

    (1) 여기서 확인을 누를 경우 event.preventDefault()가 발생하여 (숨기는)기본동작이 취소가된다.

    (2) 여기서 취소를 누를 경우 event.preventDefault()가 발생하지않아 정상적으로 실행히 된다.

  4. if(!rabbit.dispatchEvent(event))의 rabbit.dispatchEvent(event)는 확인을 눌러 기본동작이 취소되므로 dispatchEvent가 발생하지않아 false가 리턴된다.
    즉, !rabbit.dispatchEvent(event)는 확인을 누를경우 true가 되어
    alert(기본 동작이 핸들러에 의해 취소되었습니다.);가 나타난다.

  5. 취소를 누르게 될 경우 else문이 실행되어 rabbit을 숨기는 기능을 수행한다.

기본동작인 rabbit.dispatchEvent(event)와 이를 false로 만드는 event.preventDefault(); 또 이러한 preventDefault()를 사용하기위해서 필수적인 new CustomEvent의 내용인 cancelable: true를 주의하며 생각하자!!!!

위의 내용에 좀 더 변형시켜보기

  
<!DOCTYPE html>
<html>

<head>
    <title>TensorFlow.js Tutorial - lemon</title>

</head>

<body>
<pre id="rabbit">
  |\   /|
   \|_|/
   /. .\
  =\_Y_/=
   {>o<}
</pre>
<button onclick="hide_function()">hide()를 호출해 토끼 숨기기</button>
<button onclick="show_function()">show()를 호출해 토끼 보이기</button>

<script>
  // hide() will be called automatically in 2 seconds
  function hide_function() {
    let event = new CustomEvent("hide", {
      cancelable: true // cancelable를 true로 설정하지 않으면 preventDefault가 동작하지 않습니다.
    });
    if (!rabbit.dispatchEvent(event)) {
      alert(`기본 동작이 핸들러에 의해 취소되었습니다.`);
    } else {
      rabbit.hidden = true;
    }
  }

  function show_function(){
  	let event = new CustomEvent("show",{
  		cancelable:true
  	});
  	if(!rabbit.dispatchEvent(event)){
  		alert(`기본 동작이 핸들러에 의해 취소되었습니다.`);
    } else {
      rabbit.hidden = false;
    }
  }

  rabbit.addEventListener('hide', function(event) {
    if (confirm("숨기는 동작을 막는 preventDefault를 호출하시겠습니까?")) {
      event.preventDefault();
    }
  });

  rabbit.addEventListener('show', function(event) {
    if (confirm("보여주는 동작을 막는 preventDefault를 호출하시겠습니까?")) {
      event.preventDefault();
    }
  });
</script>
</body>

</html>

우리는 기본동작취소를 아니요 했을때 숨기는 기능을 구현하였다.
추가로 기본동작취소를 아니요 했을때 다시 보여지는 기능을 추가해보았다.

정상동작하였고 필요에 따라 사라진 토끼를 보이는 버튼을 preventDefault를 취소 할 경우 다시 토끼가 보여지는 결과를 실행하는 show라는 새로운 CustomEvent를 알 수 있다.

피드백을 통한 수정 부분

위의 내용에서

 
<!DOCTYPE html>
<html>

<head>
   <title>TensorFlow.js Tutorial - lemon</title>

</head>

<body>
   <h1 id="elem">Hello from the script!</h1>

<script>
 // 버블링이 일어나면서 document에서 이벤트가 처리됨
document.addEventListener("hello", function(event) { // (1)
   alert("Hello from " + event.target.tagName); // Hello from H1
 });

 // 이벤트(hello)를 만들고 elem에서 이벤트 디스패치
 let event = new Event("hello", {bubbles: true}); // (2)
 elem.dispatchEvent(event);

 // document에 할당된 핸들러가 동작하고 메시지가 얼럿창에 출력됩니다.

</script>
</body>

</html>

이와 같이 했을때와 다르게 {bubbles:true}를 지우면 결과가 나오지 않는다 했다.

주의할 것 : CustomEvent를 만들때 캡쳐링을 할 것인지 버블링을 할 것인지와 addEventListener에서의 받아내는 것이 캡쳐링을 할 것인지 버블링을 할 것인지와 같아야한다.

위의 코드는 Event를 버블링의 형태로 dispatch하였고 이를 addEventListener가 따로 설정이 없으면 false(default 값) 핸들러는 버블링 단계에서 동작합니다. 따라서 버블링 dispatch를 버블링으로 받아내어 정상적 동작을 합니다.
하지만 {bubbles:true}가 없을 경우 캡쳐링 형태로 dispatch하였고 이를 addEventListener는 버블링 단계에서 동작할게 없으므로 다른 결과를 나타냅니다.

즉, 만약 바꾸고 싶다면 둘다 캡쳐링 형태로 바꾸어야합니다.

<!DOCTYPE html>
<html>

<head>
    <title>TensorFlow.js Tutorial - lemon</title>

</head>

<body>
    <h1 id="elem">Hello from the script!</h1>

<script>
  // 버블링이 일어나면서 document에서 이벤트가 처리됨
 document.addEventListener("hello", function(event) { // (1)
    alert("Hello from " + event.target.tagName); // Hello from H1
  },{capture: true});

  // 이벤트(hello)를 만들고 elem에서 이벤트 디스패치
  let event = new Event("hello"); // (2)
  elem.dispatchEvent(event);

  // document에 할당된 핸들러가 동작하고 메시지가 얼럿창에 출력됩니다.

</script>
</body>

</html>

위와 같이 커스텀이벤트도 캡쳐링으로 바꾸고 이를 받는 addEventListener 또한 {capture: true} 설정하여 결과를 캡쳐링 하는 경우에 받을 수 있게 한다.

정리

캡쳐링과 버블링은 전부 일어난다. 단지 이벤트를 어떻게 주고 그것을 어떻게 받느냐를 같게 해야 의도한대로 실행된다.

profile
나의 하루를 가능한 기억하고 즐기고 후회하지말자

5개의 댓글

comment-user-thumbnail
2021년 1월 6일

다르게 말하면 만약 (2)와 (3)의 위치가 바뀐다면 alert함수를 수행하지 못한다. 왜냐하면 elem이 전송을 보냈는데 그 시기에 아직 해당하는 이벤트리스너가 추가 되어있지 않기 때문이다.

동기적으로 동작을 하는 코드 이군요. 직접 여러 시도를 해보는 자세가 훌륭합니다

답글 달기
comment-user-thumbnail
2021년 1월 6일

기본적으로 이벤트가 발생하면 이벤트 흐름은 document부터 시작해서 해당 위치의 이벤트 발생지점까지 캡쳐링을 하고 다시 버블링을 통해 간다면 {bubbles:true}를 삭제해도 결과가 같을거라 보는데 만약 이런 다른 결과라면 dispatchEvent는 따로 이벤트 흐름의 캡쳐링 없이 말그대로 dispatch(전송) 이므로 document부터의 전송없이 해당 이벤트 위치에 바로 도달하고 그 후 버블링을 통해 처리되지않는것이기 때문일까 개인적으로 생각을 한다. => 이에관련 내용에 의견들을 남겨주면 감사하겠습니다.

형욱님의 생각을 보고 저도 버블링과 캡쳐링을 다시 공부하게 되었는데요, https://eyabc.github.io/Doc/dev/core-javascript/Browser_Bubling_Capturing.html#%EC%BA%A1%EC%B2%98%EB%A7%81

제생각에는 버블링과 캡쳐링은 항상 일어나지만 이벤트 핸들러가 캡쳐링에서 일어나느냐 버블링에서 일어나느냐의 차이 같습니다.

1개의 답글
comment-user-thumbnail
2021년 1월 6일

잘 읽었습니다. 만약 이부분은 {bubbles:true} 부분을 생략한다면 브라우저 로드 시 addEventListener가 발생하지않는다. 부분을 학습시 당연하게 받아들이기만 했는데 덕분에 생각해볼 수 있는 계기가 되었습니다. 감사합니다.
dispatchEvent가 이벤트를 실행하게 하는거라 이해를 했습니다. elem.dispatchEvent(event)에서 'hello'라는 커스텀 이벤트가 '발생'한다고 생각했는데 elem이라는 해당요소까지 캡처링은 이루어지지만(이벤트 자체가 전파는 되나 핸들러는 작동하지 않음) 이벤트 정의시 {bubbles: true}를 명시해주지 않으면 버블링으로 전파되는 이벤트가 캐치되지 않게 하는 로직을 갖고있는 것은 아닐까 싶네요.. 적다보니 더 헷갈리네요 ㅋㅋ 물론 아닐 수도 있습니다!
+추가
저도 버블링 캡처링 다시 들여다봤는데 위 댓글의 은영님 말씀과 같이 버블링과 캡처링을 이해했습니다. 제생각에는 버블링과 캡쳐링은 항상 일어나지만 이벤트 핸들러가 캡쳐링에서 일어나느냐 버블링에서 일어나느냐의 차이 같습니다.

1개의 답글