[React] custom checkbox 만들기 (이벤트 버블링과 캡쳐링🌟)

Noeyso·2023년 4월 11일
0

프로젝트

목록 보기
7/7
post-thumbnail

개요

프로젝트에서 체크박스를 Figma 디자인에 맞게 커스텀하는 과정에서 몇가지 이슈가 있었습니다. 간단한 예제를 작성하여 다시 복습해보며 문제 상황들을 정리해보겠습니다.

기존 체크박스


기존의 체크박스는 다음과 같이 동작합니다.
1. 체크박스를 클릭하면 체크가 되고 외부 박스의 테두리가 생긴다.
2. 체크박스의 동작과 관계없이 박스의 클릭 이벤트가 발생하여 '박스가 체크되었습니다'라는 문구가 생겨난다.

이러한 기존의 체크박스를 다음과 같이 개선할 것입니다.✨
1. 체크박스의 디자인을 변경한다.
2. 체크박스가 클릭될때 외부 박스의 클릭 이벤트를 막는다.

커스텀 체크박스 만들기 ✅

input태그 자체를 커스텀하는 것은 한계가 있습니다. label을 사용해서 체크박스를 새로 만들어주겠습니다.

<div class='box'>
  <input id='checkbox' type='checkbox' />
  <label htmlFor='checkbox'></label>
</div>

id와 htmlFor에 같은 값을 줘서 인풋과 라벨을 연결해줍니다.
(** 참고로 html에서는 htmlFor가 아니라 for 속성입니다.)

이제 css를 작성해봅시다. 스타일이 핵심입니다. 🌟

input {
  display: none;
}
label{
  display: flex;
  width: 30px;
  height: 30px;
  border: 2px solid gray;
  cursor: pointer;
}
input:checked + label::after{
  content:'✔';
  width: 30px;
  height: 30px;
  font-size: 20px;
  text-align: center;
  background-color: white;
}

기존 input은 보이지 않게 none 처리를 해줬습니다.( visibility:hidden 처리를 하게되면 보이지는 않지만 공간은 남아있기때문에 display:none처리를 해줍니다. )
input이 checked 됐을때, 체크박스 안에 체크표시가 보일 수 있게 after를 사용해서 가상요소를 추가했습니다.

이렇게하면

짠 🙌
체크박스를 원하는 디자인으로 변경해주었습니다.

하지만, 아직 한가지 처리할 이슈가 남아있습니다. 체크박스가 클릭될때 박스의 클릭 이벤트가 발생하여 '박스가 체크되었습니다' 가 무한정 찍히는 것을 볼 수 있는데요.
이런 현상이 나타나는 원인이 바로 이벤트의 버블링/캡처링 현상입니다.

구현도 중요하지만 개념도 중요하기에 간단하게 개념 짚고 넘어가겠습니다.

이벤트 버블링과 캡처링

버블링(Bubbling)

이벤트 버블링이란 한 요소에 이벤트가 발생하면 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 이 과정이 반복되며 최상단의 부모 요소를 만날 때까지 핸들러가 동작한다.

캡처링(Capturing)

캡처링은 버블링과 반대로 최상위 태그에서 해당 태그를 찾아 내려가는 것이다.

현재 프로젝트에 빗대어 설명해보겠습니다.
👆check box 클릭!
이후 다음과 같은 동작이 발생합니다.
1. div -> input 이벤트가 발생한 태그를 찾아낸다. (캡처링)
2. input 태그의 이벤트 핸들러가 동작하여 change 이벤트 발생
3. input의 부모인 태그 div의 이벤트 핸들러도 동작하여 click 이벤트 발생 (버블링)


원인을 알았습니다. 그럼 이제 이 이벤트 버블링과 캡처링을 막는 방법을 찾아봅시다.

첫번째 방법 : stopPropagation

가장 많이 사용되는 방식입니다. 해당 요소에 event.stopPropagation() 과 같이 함수를 호출하여 부모 이벤트를 무시하는 방법입니다.

const clickInputTag =(e)=>{ 
	e.stopPropagation();
}

위 함수를 input 태그의 click 이벤트 함수로 등록해주면 됩니다.
그런데 이렇게 등록해도 여전히 '박스가 체크되었습니다'가 찍히고 있는 것을 볼 수 있을 것입니다. 이는, 우리가 input 체크박스를 커스텀하면서 label도 정의해줬기 때문입니다. 따라서 label의 클릭 이벤트 함수에서도 stopPropagation을 등록해줘야 합니다.

부모의 이벤트 함수가 실행되지 않습니다. 👍

두번째 방법 : target , currentTarget ✨

stopPropagation을 사용한 경우, input과 label에 클릭 이벤트를 등록해서 이벤트 전파를 막아주었습니다.
이번엔 간단하게 부모 요소에서 이벤트 전파를 방지하는 방법을 알려드리겠습니다. 바로 event target을 비교하는 것입니다.
이 방법을 사용하기 위해서 event의 target과 currentTarget의 개념에 대해 잠깐 짚고 넘어가겠습니다.

target

이벤트가 발생한 대상 객체를 가리킵니다

currentTarget

event Handler가 등록된 요소를 반환한다.

위 예제에서 체크박스 외부 박스를 클릭하는 함수내에서 다음과 같이 target과 currentTarget을 출력해보겠습니다.

const clickBox = (e) => {
  console.log(`target: ${e.target}, currentTarget: ${e.currentTarget} `)
}


체크박스를 클릭하고 console 창을 확인해보니 target 값과 currentTarget 값이 다르게 나타나는 것을 확인할 수 있습니다.

target은 현재 클릭한 label 요소이고 currentTarget은 클릭 이벤트 함수가 등록된 div 요소네요.

이를 활용해서 우리는 이벤트 전파를 막을 수 있습니다.

부모 이벤트 함수에서 이벤트 전파 방지하기

const clickBox = (e) => {
  if(e.currentTarget!==e.target){return;}
}

간단하죠? 부모 요소에 위와 같이 currentTarget과 target이 다른 경우 return하는 조건문을 추가해줌으로써 이벤트가 전파되는걸 방지했습니다.
실행해보면 체크박스 체크 시 외부 이벤트 함수가 동작하지 않는 것을 확인할 수 있을 것입니다.


마무리

이번 포스팅에서는 커스텀 체크박스 만들기를 시작으로 이벤트 버블링과 캡처링에 대한 개념까지 알아보았습니다.
stopPropagation을 사용할 경우, 부모 요소의 모든 이벤트를 막기 때문에 규모가 큰 프로젝트에서는 두번째 방법을 사용해서 부모 요소에서 전파를 방지하는 것이 좋을 것 같다는 생각이 듭니다.

profile
React,Typescript를 공부하고 있는 주니어 프론트엔드 개발자입니다. 👩🏻‍💻

0개의 댓글