하나만 골랐는데 전체 선택도 체크돼 보입니다

자료 세 개 중 하나를 골랐을 때 전체 선택을 빈칸으로 두면 아무것도 고르지 않은 것처럼 보일 수 있습니다. 반대로 체크 표시를 넣으면 전부 선택한 것처럼 읽힙니다. 상위 체크박스가 집합을 요약한다면 없음·일부·전부의 세 상태를 표현해야 합니다.

예제는 안내문, 구성표, 치수 도식의 선택 목록입니다. 개별 체크박스는 두 상태이고, 전체 선택은 이 목록을 요약하고 바꿉니다. 실제 파일 다운로드를 실행하는 목록은 아닙니다.

부분 선택은 별도의 상품 값이 아닙니다

세 항목을 요약하는 상태
선택 수전체 선택의 표시현재 뜻
0/3unchecked고른 항목 없음
1/3 또는 2/3indeterminate일부만 선택
3/3checked모두 선택

MDN은 indeterminate가 checked와 별개의 속성이며 표시 상태를 바꾼다고 설명합니다. 실제 폼 전송에서 어떤 체크박스의 값이 포함되는지는 checked에 달려 있습니다. 부분 선택 표시를 넣었다고 그 자체가 서버로 보내는 세 번째 값이 되는 것은 아닙니다.

상위 체크박스를 집계용으로만 쓸지 실제 필드로 전송할지도 정합니다. 보통 개별 선택값이 목적이라면 상위에는 불필요한 name을 주지 않고, 현재 선택한 항목의 목록을 처리하는 편이 의미가 분명합니다.

자식 선택이 바뀔 때 상위를 다시 계산합니다

function syncSelection() {
  const selected = items.filter(item => item.checked).length;
  master.disabled = items.length === 0;
  master.checked = items.length > 0 && selected === items.length;
  master.indeterminate = selected > 0 && selected < items.length;
  summary.textContent = `${selected}/${items.length}개 선택`;
}

HTML에 indeterminate라는 속성을 적는 방식이 아니라 자바스크립트의 속성으로 설정합니다. 선택 수를 계산한 뒤 전부인지와 일부인지를 각각 정합니다. 목록이 0개일 때 every() 결과만 보고 모두 선택으로 만들지 않도록 빈 목록도 분리했습니다.

행을 추가하거나 지웠다면 items가 현재 조작 가능한 항목을 담는지 다시 확인해야 합니다. 품절·비활성 항목을 집계에 포함할지도 정책입니다. 예제는 활성 항목 세 개만 사용하며, 실제 목록에서는 분모를 무엇으로 삼는지 이름이나 요약에서 알 수 있어야 합니다.

상위 실행은 어떤 결과를 만들지 일관되게 정합니다

master.addEventListener('change', () => {
  items.forEach(item => { item.checked = master.checked; });
  syncSelection();
});
items.forEach(item => item.addEventListener('change', syncSelection));

예제에서는 부분 선택 상태의 전체 선택을 실행하면 모두 선택하고, 다시 실행하면 모두 해제합니다. 이전에 고른 부분 조합을 기억해 복원하는 별도 기능을 넣을 수도 있지만 그 규칙을 조용히 섞지 않습니다. W3C의 체크박스 패턴은 집합을 요약하는 세 상태와 상위 실행을 설명합니다.

색상과 체크 표시만 보지 말고 “1/3개 선택”처럼 현재 수를 텍스트로 남길 수 있습니다. 기본 체크박스와 연결된 레이블을 사용하고 Space로 바꾸었을 때도 같은 집계가 나타나는지 확인합니다.

한 개 선택에서 시작해 전부와 해제를 왕복합니다

  • 처음 한 개를 고르면 상위의 부분 선택과 1/3 요약이 함께 나타난다.
  • 전체 선택을 실행하면 세 항목과 3/3 요약이 맞는다.
  • 한 항목을 해제하면 다시 부분 선택으로 돌아간다.
  • 전부 해제하면 상위도 빈 상태가 된다.

개별 복수 선택을 라디오와 구별하는 내용은 선택 규칙과 입력 요소가 다룹니다. 이 글에서는 그 선택 집합을 요약하는 상위 컨트롤이 실제 항목과 같은 상태인지 끝까지 연결합니다.

예제를 직접 열어 보기

이 글의 화면 예제와 소스 받기 ZIP. 압축을 푼 뒤 index.html을 브라우저에서 열 수 있습니다. 예제의 입력과 표시 범위는 각 화면의 안내를 기준으로 확인하세요.

참고한 원문