JS 이벤트(2024-11-18 수업)

짝은별·2024년 11월 18일

JS

목록 보기
14/23

debounce 와 throttle

우선 debouncethrottle에 대해 생각하기 전에 하나 알고 갈 것이 있다
만약 비동기(setTimeout)으로 선언된 것은 clearTimeout으로 타이머를 초기화 시킬 수 있다

그렇다면 debouncethrottle은 무엇일까?

만약 form태그input이벤트를 주고 inputvalue를 출력한다고 가정해보자
그러면 input내부의 값이 바뀔때마다 계속해서 최신화하며 출력한다
하지만 이는 많은 비용의 통신값이 든다

따라서 이를 해결하고자 고안된 개념이 debouncethrottle이다
debounce실행이 완료된 직후부터 타이머를 시작하고,
throttle실행이 시작한 직후부터 타이머를 시작한다

참고로 debouncetimer가 완료되기 전새로운 실행이 생기면 timer가 초기화된다

이를 이용해 debounce를 구현해보자

function debounce(callback, limit = 500) {
  let timeout;

  return function (e) {
    clearTimeout(timeout);

    timeout = setTimeout(() => {
      callback.call(this, e);
    }, limit);
  };
}

target.addEventListener('mousemove', debounce(handleMove, 100));

참고로 handleMove새로운 이모티콘을 생성하는 함수라고 생각하면 된다
이때 debounce이벤트로 할당했고 이때 callback 함수로는 handleMove를, limit의 값으로는 0.1s(100ms)를 넘겨줬다
함수가 실행됐기 때문에 return이 할당되므로
mousemove가 일어나는 즉시 clearTimeout이 실행된다(이때 타이머 초기화)

이후 setTimeout으로 다시 타이머를 실행시켜 debounce가 가능하게 하는 것이다.

이번엔 throttle을 구현해보자

function throttle(callback, limit = 500) {
  let wait = false;

  return function (...args) {
    if (!wait) {
      callback.apply(this, args);
      wait = true;
      setTimeout(() => {
        wait = false;
      }, limit);
    }
  };
}

target.addEventListener('mousemove', throttle(handleMove, 1000));

debounce와 크게 다르진 않지만 차이는 확실하다
가장 큰 차이점timer를 초기화 시키지 않고 현재의 상태를 저장한다는 점에 있다

현재 동작이 가능한지를 확인하는 상태를 저장하는 변수 wait을 설정해준다
그리고 이후 return이 할당되어 wait이 false(현재 사용하지 않은 상태)라면 wait을 true로 바꿔주고
setTimeout을 이용해 함수의 타이머를 다시 실행시키는 함수를 사용한다

버블링과 캡쳐링

버블링캡쳐링이벤트의 상속과 관련이 있다

버블링은 만약 부모에게 이벤트가 할당이 된다면 자식에게까지 영향을 미치는 것을 의미한다
이는 코드와 결과를 확인하며 살펴보자

우선 HTML의 구조는 다음과 같다

    <div class="grandParent">
      조상
      <div class="parent">
        부모
        <div class="child">자식</div>
      </div>
    </div>

조상 밑에 부모 그리고 그 밑에 자식이 있다
이번엔 JS를 이용해 click시 class이름을 alert로 표시해주는 이벤트를 각각에 할당해보겠다

const grandParent = document.querySelector('.grandParent');
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');

grandParent.addEventListener('click', () => alert('grandParent'));

parent.addEventListener('click', () => alert('parent'));

child.addEventListener('click', () => alert('child'));

그냥 러프하게 짰다

그렇다면 원래의 생각은 조상의 영역을 클릭하면 'grandParent'
부모의 영역을 클릭하면 'parent'
자식의 영역을 클릭하면 'child'를 alert로 출력해줘야 한다

하지만 결과는 다음과 같다

  • 조상 영역 클릭시

  • 부모 영역 클릭시

  • 자식 영역 클릭시

이처럼 상속되는 모습을 확인할 수 있다
이를 버블링이라고 부른다

이는 가장 최상단의 조상요소를 만날때까지 반복되며 실행되고 이때 각각의 요소에 할당된 이벤트 핸들러들이 실행된다

그렇다면 캡쳐링은 무엇일까?
버블링과 반대의 개념이다
버블링자식에서부터 조상까지 도달했다면 캡쳐링조상에서부터 자식에게까지 접근한다

이는 addEventListener함수에 3번째 인자true 혹은 {capture:true}를 사용하여 캡쳐링을 가능하게 한다 (참고로 true는 {capture:true}의 축약형이다)
참고로 capturetrue의 값을 갖는 요소들의 이벤트 핸들러만 작동한다

만약 bubbling을 방지하고 싶다면 event.stopPropagation()을 사용하여 가능하다
하지만 이는 데이터 수집에 방해가 되므로 사용을 권장하진 않는다

target, currentTarget

이는 이벤트동작했을때 발생한 대상을 찾는 method이다

하지만 차이점은 target현재의 영역을 반환하고 currentTarget이벤트를 호출한 영역을 반환한다

아까의 이벤트를 할당한 코드를 약간 수정하여 target과 currentTarget의 차이를 살펴보자

const grandParent = document.querySelector('.grandParent');
const parent = document.querySelector('.parent');
const child = document.querySelector('.child');

grandParent.addEventListener('click', (e) => {
  alert('grandParent');
  console.log(e.target);
  console.log(e.currentTarget);
});

parent.addEventListener('click', (e) => {
  alert('parent');
  console.log(e.target);
  console.log(e.currentTarget);
});

child.addEventListener('click', (e) => {
  alert('child');
  console.log(e.target);
  console.log(e.currentTarget);
});

단순히 이벤트의 targetcurrentTargetconsole창에 띄우는 코드를 추가한 것 외엔 변화가 없다

그렇다면 결과를 확인해보자

  • 조상 영역 선택 시

    이벤트를 실행한 곳도 클릭한 현재의 영역도 grandParent로 변화가 없다

  • 부모 영역 선택 시

    처음엔 parent에서 시작하므로 둘 다 똑같다 그러나 조상의 이벤트를 호출할때 currentTarget은 이벤트를 실행한 요소인 grandParent를 출력한다

  • 자식 영역 선택 시

    이도 역시 부모 영역 선택 시와 같은 흐름의 결과를 출력하고 있다

마지막으로 thiscurrentTarget의 관계를 알아보겠다
일반함수로 이벤트 함수를 작성했다면 this currentTarget과 값이 일치한다
하지만 화살표 함수로 작성했다면 this상위 context의 this가 할당되기에
위의 코드에선 window객체가 할당된다
따라서 화살표 함수에선 currentTargetthis일치하지 않는다

profile
FE(철 아님) 개발자 꿈꾸다

0개의 댓글