[JavaScript] 체크박스 기능 종류별로 뽀개기

Moon Hee·2022년 8월 20일
0

트러블 슈팅

목록 보기
21/26
post-thumbnail

장바구니 페이지는 역시 금방 끝낼 수 없었다. 체크박스일 뿐인데 시간이 좀 걸렸다. 회원가입이나 로그인 페이지처럼 한 페이지를 한 파일로 끝낸 경우에는 script를 import해서 복잡한 기능을 쉽게 처리할 수 있었는데, 이번 장바구니 페이지는 컴포넌트로 분리되어 있다. 그래서 체크박스도 제목행에 있는 체크박스와 장바구니 상품별 체크박스를 구분해서 기능을 만들어야 했다.

1. 제목행 체크박스는 CartList 클래스에서 생성
2. 장바구니 상품별 체크박스는 CartListItem 클래스에서 생성

1. (공통) 체크박스 클릭 시 토글 기능


// CartList(제목행 체크박스)
buttonCartCheck.addEventListener("click", () => {
    buttonCartCheck.classList.toggle("fill");
});

// CartListItem(장바구니 상품별 체크박스)
checkButton.addEventListener("click", () => {
    checkButton.classList.toggle("fill");
});

css에서 fill 클래스에 대한 스타일을 만들어 놓고 toggle 기능을 추가하는 간단한 코드이다. CartList 클래스에는 이 기능만 추가하고 나머지 체크박스 기능은 CartListItem 클래스에서 처리했다.


2. 전체선택 및 전체삭제


const allCheckBox = document.querySelectorAll(".button-cart-check");
let itemAllCheck = false;

for (let i = 0; i < allCheckBox.length; i++) {
    allCheckBox[0].addEventListener("click", () => {
        // 제목행 체크되어 있으면 나머지도 체크
        if (allCheckBox[0].className.includes("fill")) {
            allCheckBox[i].classList.add("fill");
            itemAllCheck = true;
        // 제목행 체크해제하면 나머지도 해제
        } else {
            allCheckBox[1].classList.remove("fill");
            allCheckBox[i] && allCheckBox[i].classList.remove("fill");
            itemAllCheck = false;
        }
    });
...(3.으로 이어짐)

allCheckBox 변수로 체크박스 클래스를 잡아줬다. 제목행 체크박스와 장바구니 상품별 체크박스 모두 ".button-cart-check"으로 처리했다. itemAllCheck 변수로는 제목행 체크박스 포함 모든 체크박스의 상태를 나타내고 있다.


3. 전체 선택 후 상품 하나라도 해제하면 제목행 체크 해제하기


for (let i = 0; i < allCheckBox.length; i++) {
  ...(2.내용)

    if (itemAllCheck) {
        !allCheckBox[i].className.includes("fill") &&
            allCheckBox[0].classList.remove("fill");
        itemAllCheck = false;
    }
  
  });

itemAllCheck가 true인 상태일 경우로 전체 선택 상황을 만들고, 어떤 체크박스든 클래스명에 fill을 포함하고 있지 않으면 제목행 클래스명에서 fill을 제거하는 방식으로 처리할 수 있었다. 여기까지 매우 간단했다.


4. 상품이 모두 선택되는 경우 제목행 체크하기


for (let i = 0; i < allCheckBox.length; i++) {
  ...(2.내용)
  
  allCheckBox[i].addEventListener("click", () => {
    // 1) NodeList 배열화
    const arrAllCheckBox = Array.from(allCheckBox);
    // 2) className 배열 변수화
    const arrClassNameAllCheckBox = arrAllCheckBox.map(
        (item) => item.className
    );
    // 3) 배열 맨앞 요소 제거
    arrClassNameAllCheckBox.shift();
	// 4) 모든 배열에 fill 문자가 있는지 확인
    let everyElHasFill = arrClassNameAllCheckBox.every((el) => {
        return el.includes("fill");
    });

    // 3.의 내용
    if (itemAllCheck) {
        !allCheckBox[i].className.includes("fill") &&
            allCheckBox[0].classList.remove("fill");
        itemAllCheck = false;
    } else {
        everyElHasFill
            ? allCheckBox[0].classList.add("fill")
            : allCheckBox[0].classList.remove("fill");
    }
  });

장바구니 상품별로 체크박스가 있는데, 그 체크박스들이 개별적으로 모두 선택이 되는 경우도 있으므로 그 경우에는 제목행을 컨트롤해야 했다. 어떻게 모두 체크되었는지 확인할 수 있을지 그 방법을 많이 고민했다. 나는 클래스명으로 체크여부를 확인할 수 있다는 것을 활용해서 클래스명을 모두 배열로 반환했다.

querySeletorAll로 돔을 잡게되면 NodeList로 확인되는데, 이 NodeList는 DOM API가 여러 개의 결과 값을 반환하기 위한 DOM 컬렉션 객체이다. 즉, 배열처럼 확인이 되도 곧바로 배열로 사용할 수는 없으므로 변환을 해줘야 한다.

// 위와 같은 NodeList를 진짜 배열로 만들기 위한 방법
Array.from(querySeletorAll을 잡아준 변수)

Array.from으로 변환시켜주면 아래 그림처럼 확인된다. 모양은 똑같지만 배열 메서드를 사용하기 위해서는 이렇게 변환을 꼭 시켜줘야 한다.

지금은 노드 자체를 잡은것에 불과하기 때문에 그 안으로 들어가서 클래스명만 남긴다. item을 찍어서 확인해보면 여러 속성들을 잡을 수 있다. 지금 필요한 내용은 className이다. 이렇게 map으로 잡아주면 각 체크박스 노드의 클래스명만 배열에 남게 된다. 이 배열만 남은 것도 변수화 해준다.

arrAllCheckBox.map((item) => item.className);
// 결과 예시: ['button-cart-check fill', 'button-cart-check', 'button-cart-check']

const arrClassNameAllCheckBox = arrAllCheckBox.map(
    (item) => item.className
);

그리고 또 중요한 것. 제목행 체크박스의 상태는 결과이지 조건에 들어가지 않으므로 배열의 첫번째 요소를 제거해서 사용한다. shift()를 쓰면 첫 요소를 삭제할 수 있다. 마지막 요소를 제거하는 건 pop()이다.

// 배열 맨앞 요소 제거 후 변수 사용(메서드 포함해서 사용하지 않는다)
arrClassNameAllCheckBox.shift();

마지막으로 배열의 모든 요소에 "fill"이 있는지 확인하는 메서드를 사용해서 everyElHasFill 이라는 변수에 저장한다.

let everyElHasFill = arrClassNameAllCheckBox.every((el) => {
    return el.includes("fill");
});

염두에 둬야 하는 점은, 장바구니 데이터를 이 클래스에서 세팅하기 때문에 결과가 여러번 반복된다는 것이다. 예를 들어, 장바구니에 담긴 상품이 3가지면 함수가 3번 호출되는 식이다. 각 실행 값이 다를 수 있다. false-true-false 이런 식으로. 복잡해 보이지만 마지막 값으로 처리를 하면 되는 거였다.

그래서 everyElHasFill의 불리언 값을 사용해서 제목행 체크박스를 컨트롤 하려면 everyElHasFill 값이 true일 때와 false일 때를 모두 대응해서 어떻게 처리해야 할지 코드를 짜면 된다.

만약에 everyElHasFill && allCheckBox[0].classList.add("fill") 처럼 작성할 경우 true가 한번이라도 나오면 제목행이 체크되어 버린다. 마지막 값이 실제와 부합하므로 삼항연산자를 사용해서 false까지 처리해줘야 한다.

else {
    everyElHasFill ? allCheckBox[0].classList.add("fill")
    : allCheckBox[0].classList.remove("fill");
}

장바구니 체크박스일 뿐인데 필요한 배열을 찾고 활용하는 데 시간이 걸리는 것 같다. 한번 해봤으니 다음에는 더 빨리 생각났으면 좋겠다. 이번 달까지 데브 스토어 완성할 수 있을까?;

profile
프론트엔드 개발하는 사람

0개의 댓글