이벤트 위임을 이용할 땐 확실히 찝어주자

삼콩·2022년 5월 31일
1

JavaScript

목록 보기
4/6
post-thumbnail

1. 냅다 구현한 코드

    <script>
      //우선 버튼과 li들을 담고 있는 ul을 변수에 저장해준다 
      const btn = document.querySelector(".btn-select");
      const langList = document.querySelector(".list-member");
      btn.addEventListener("click", () => {
        langList.classList.toggle("on");
        btn.classList.add("on");
      });
      // btn-select에 textContent 삭제하고 li textContent 를 넣기
      const li = langList.children;
      for (let i = 0; i < li.length; i++) {
        li[i].addEventListener("click", () => {
          btn.textContent = li[i].textContent;
          langList.classList.remove("on");
          btn.classList.remove("on");
        });
      }
    </script>

물론 이 방법도 좋지만, for문을 하나하나 돌아줘야한다는 단점이 있다. 이벤트 위임은 공통 조상에 이벤트 핸들러를 하나만 사용해도, 여러 요소를 한꺼번에 다룰 수 있는 것을 말하는데, 이벤트 위임을 사용하면 저렇게 자식요소들이 많아져도 하나하나 줄 필요가 없어서 좋다! 따라서 이벤트 위임을 활용해 코드를 좀 더 간결하게 짜보면 다음과 같다.

2. 캬 난 이벤트 위임을 사용할 줄 알아~!

    <script>
      //btn-select를 클릭하면 list-member on 클래스 toggle로 달기
      const btn = document.querySelector(".btn-select");
      const langList = document.querySelector(".list-member");
      btn.addEventListener("click", () => {
        langList.classList.toggle("on");
        btn.classList.add("on");
      });
      // btn-select에 textContent 삭제하고 li textContent 를 넣기
      langList.addEventListener("click", () => {
        btn.textContent = event.target.textContent;
        langList.classList.remove("on");
        btn.classList.remove("on");
      });
    </script>

물론 코드는 많이 간결해졌지만, 문제는 저 빨간색 부분을 클릭하면 발생하는 일이다.


히히히히... 죄다 불러와버렷~! 이런일이 발생한 이유는 내가 이벤트 위임을 활용해 해냈다는 것에 대한 뿌듯함이 내 눈을 가려버린것 ... 사실 저 부분은 버튼이 아닌 ul부분이기 때문에 event.target이 ul이 되어 ul에 있는 모든 textContent요소가 btn에 후두두둑 담겨버린다.

3. 더 깔끔한 코드를 작성해보자

    <script>
        const btn = document.querySelector('.btn-select');
        const list = document.querySelector(".list-member");
		//추후 동적으로 데이터를 추가해줄 수 있도록 배열로 담아둔다
        const arrLang = ['neo', 'Java', 'JavaScript', 'C#', 'C/C++'];
		//forEach는 아이템을 순회하며 실행해준다
        arrLang.forEach((item) => {
        	//우선 li,button 태그를 생성해준다
            //button 태그를 사용해주는 이유는
            //시맨틱한 마크업을 작성해주기 위함이다.
            const li = document.createElement('li');
            const btn = document.createElement('button');
            // setAttribute는 선택한 요소(element)의 속성값을 정한다
            //<button type="button"></button>
            btn.setAttribute('type', 'button');
            //btn의 textContent를 item에서 가져와 넣어준다
            btn.textContent = item;
            //li를 list의 자식 요소로 넣어준다
            //btn을 li의 자식 요소로 넣어준다
            list.appendChild(li);
            li.appendChild(btn);
        });
		//toggle을 통해 btn을 클릭했을 때
        //classList에 on이 없으면 추가해주고
        //on이 있으면 제거해주는 기능
        btn.addEventListener('click', () => {
            btn.classList.toggle('on');
        });
		//여기가 중요한데, 이벤트 위임을 통해 처리해주는 대신
        //ul이 선택되지 않도록,
        //if 조건문을 달아서 event가 발생된 target의
        //nodeName이 버튼일 경우에만 textContent를 넘겨주면 된다.
        //list를 클릭하면 btn을 닫을 수있도록 remove로 클래스를 지워준다
        list.addEventListener('click', (event) => {
            if (event.target.nodeName === 'BUTTON') {
                btn.textContent = event.target.textContent;
                btn.classList.remove('on');
            }
        });
    </script>

이와 같이 처리해준다면, 이벤트 위임을 이용하면서 동시에 예외 처리를 통해 버튼일 경우에만 동작이 되도록 처리해줘서 정상적으로 작동한다 :-) 야호

참고자료
https://ui.toast.com/weekly-pick/ko_20160826

깨달은 점

👉 노드네임을 통해 예외처리 해주기!

👉 예외 처리는 확실히 혼자 발견하기 어려운 것 같다. 피드백의 중요성을 다시 한번 느꼈다.

다음에 해보고 싶은 시도

👉 이벤트 전파를 막는 event.stoppropagation()을 사용해보기

profile
프론트엔드 세계의 모략을 꾸미는 김삼콩입니다

0개의 댓글