본문으로 건너뛰기

골라진 칸과 :has()

HTML·CSS 목차선택자레슨 48 / 93

목차

:checked는 지금 골라진 라디오 · 체크박스를 고르는 가상 클래스이고, :has()는 괄호 안 선택자에 맞는 요소를 안에 가진 요소를 고르는 가상 클래스입니다. 둘을 함께 쓰면 칸의 상태로 그 칸을 감싼 줄 전체를 바꿀 수 있습니다.

할 일 목록에서 체크한 줄에 취소선을 긋고 싶다고 합시다. 그런데 체크되는 것은 칸(input)이고, 취소선을 그을 곳은 그 칸을 감싼 줄(li)입니다. 지금까지의 선택자는 바깥에서 안쪽으로만 골랐습니다. 안에 든 것의 상태로 바깥 요소를 고르는 길이 :has()입니다. 이 레슨의 새 문법은 이 둘뿐입니다.

6단계 중 1단계. :checked는 지금 골라진 칸입니다. 아직 고른 칸이 없어 :checked + label은 0개입니다.

1:checked — 지금 골라진 칸

:checked는 라디오 · 체크박스 가운데 지금 골라진 칸을 고릅니다. 아래 잰 값은 '네'를 누른 뒤 잰 것입니다.

<input
  id="yes"
  type="radio"
  name="again"
  value="yes"
/>
<label for="yes">네</label>
<input
  id="no"
  type="radio"
  name="again"
  value="no"
/>
<label for="no">아니요</label>
:checked + label {
  font-weight: bold;
}
결과 화면
잰 값개발자 도구에서 본 값
  • :checked + label의 고른 글네
  • #yes의 :checked켜짐
  • #no의 :checked꺼짐

:checked + label은 '골라진 칸 바로 뒤의 label'입니다. +는 바로 뒤 형제 하나라, 칸 바로 다음에 쓴 짝 라벨이 굵어집니다. 결과 화면에서 '아니요'를 눌러 보세요. 굵은 글이 '아니요'로 옮겨 갑니다 — 고른 칸이 바뀌면 브라우저가 다시 고릅니다(브라우저 규칙 4).

HTML의 checked 속성은 처음 골라 둘 칸을 적는 자리이고, :checked는 누르는 대로 바뀌는 지금 상태를 봅니다.

2:has() — 안에 가진 요소

:has()는 괄호 안 선택자에 맞는 요소를 안에(후손으로) 가진 요소를 고릅니다. 아래 잰 값은 둘째 칸을 누른 뒤 잰 것입니다.

<li>
  <input
    type="checkbox"
    aria-label="완료 표시"
  />
  우유 사기
</li>
<li>
  <input
    type="checkbox"
    aria-label="완료 표시"
  />
  산책하기
</li>
li:has(input:checked) {
  text-decoration: line-through;
}
결과 화면
잰 값개발자 도구에서 본 값
  • li:has(input:checked)의 고른 글산책하기
  • li:first-child의 text-decoration-linenone
  • li:last-child의 text-decoration-lineline-through

li:has(input:checked)⁠는 '체크된 input을 안에 가진 li'입니다. 오른쪽부터 읽으면 li 가운데 괄호 안 조건을 품은 것만 남습니다. 체크를 풀면 그 줄도 다시 풀립니다.

괄호 안을 결합자 없이 쓰면 그 요소의 안쪽만 찾습니다 — 나무에서 아래로 봅니다(브라우저 규칙 2). :has()의 무게도 괄호 안 선택자에서 옵니다 — 9단원 명시도에서 셉니다.

예제

칸 하나의 상태로 바꿀 곳 고르기

체크박스 하나의 상태로 세 곳을 바꿉니다. 아래 잰 값은 칸을 누른 뒤 잰 것입니다.

<li>
  <input
    id="milk"
    type="checkbox"
  />
  <label for="milk">우유</label>
</li>
input:checked {
  accent-color: #6e6e73;
}

input:checked + label {
  color: #6e6e73;
}

li:has(input:checked) {
  text-decoration: line-through;
}
결과 화면
잰 값개발자 도구에서 본 값
  • #milk의 accent-colorrgb(110, 110, 115)
  • label의 글자색rgb(110, 110, 115)
  • li의 text-decoration-lineline-through
바꿀 곳선택자칸과의 관계
칸 자체input:checked그 요소
칸 바로 뒤 글input:checked + label바로 뒤 형제
칸을 감싼 줄li:has(input:checked)안에 가진 조상

할 일 화면은 이 길 대신 체크할 때 JavaScript가 줄에 done 클래스를 붙이고 .done .text로 그립니다. 같은 클래스로 '완료' 필터와 남은 개수까지 함께 바꾸기 때문입니다(자바스크립트 과목).

자주 틀리는 곳

틀린 생각: 체크한 줄을 고르려면 li:checked라고 쓴다.

<li>
  <input
    type="checkbox"
    aria-label="완료 표시"
  />
  우유 사기
</li>
li:checked {
  text-decoration: line-through;
}
결과 화면
잰 값개발자 도구에서 본 값
  • input의 :checked켜짐
  • li의 text-decoration-linenone

칸은 체크되었는데 줄에 취소선이 없습니다. 골라지는 것은 칸이지 li가 아니라서 li:checked는 아무것도 고르지 않습니다.

맞는 규칙: :checked는 라디오 · 체크박스처럼 골라지는 칸에만 맞습니다. 칸을 감싼 줄은 li:has(input:checked)⁠로 고릅니다(브라우저 규칙 4).

틀린 생각: :has()는 짝 라벨처럼 곁에 있는 요소도 찾는다.

<input
  id="milk"
  type="checkbox"
/>
<label for="milk">우유</label>
label:has(:checked) {
  font-weight: bold;
}
결과 화면
잰 값개발자 도구에서 본 값
  • #milk의 :checked켜짐
  • label의 font-weight400

label은 칸의 짝이지만 칸을 안에 가지지 않고 곁에 둔 형제입니다. 그래서 label:has(:checked)는 이 라벨을 고르지 않습니다.

맞는 규칙: :has(:checked)처럼 쓰면 그 요소 안(후손)만 봅니다. 곁의 형제는 +로 고릅니다(:checked + label).

확인 문제

1. 칸이 처음부터 체크된 할 일 한 줄입니다. CSS의 li:has(…)를 section:has(…)로 바꾸면 회색이 되는 요소를 모두 고르세요. 기호를 다 쓰세요.

<section>
  <h2>오늘 할 일</h2>
  <ul>
    <li>
      <input
        type="checkbox"
        aria-label="완료 표시"
        checked
      />
      우유 사기
    </li>
  </ul>
</section>
section:has(input:checked) {
  color: #6e6e73;
}
  • ㉠ h2 오늘 할 일
  • ㉡ li 우유 사기
  • ㉢ 없음
힌트

:has() 앞에 붙은 요소가 무엇으로 바뀌었는지 보세요.

정답

㉠ ㉡ — 제목과 줄 모두입니다.

결과 화면
잰 값개발자 도구에서 본 값
  • h2의 글자색rgb(110, 110, 115)
  • li의 글자색rgb(110, 110, 115)
  • section:has(input:checked)⁠는 '체크된 칸을 안에 가진 section'이라 칸을 품은 영역 전체를 고릅니다(브라우저 규칙 4). 영역의 글자색이 바뀌면 안의 제목과 줄이 그 색을 물려받습니다.
  • 흔한 오답은 ㉡만입니다. 체크한 줄만 바뀐다고 본 것입니다. 바뀌는 요소는 :has() 앞에 쓴 요소입니다.

CSS의 li:has를 section:has로 바꿔 보세요. 제목의 색을 보세요.

2. 체크된 칸을 안에 가진 줄을 고르려 합니다. ____ 자리를 채우세요.

li:has(____) {
  text-decoration: line-through;
}
힌트

괄호 안에는 줄 안에서 찾을 것(지금 골라진 칸)을 선택자로 씁니다.

정답

input:checked

<li>
  <input
    type="checkbox"
    aria-label="완료 표시"
    checked
  />
  우유 사기
</li>
li:has(input:checked) {
  text-decoration:
    line-through;
}
결과 화면
잰 값개발자 도구에서 본 값
  • li의 text-decoration-lineline-through

다르게 써도 되는 곳: :checked만 써도 같습니다. 줄 안에 골라지는 칸이 체크박스뿐이기 때문입니다.

  • 흔한 오답 checked는 콜론이 없어 checked라는 태그를 찾습니다. 그런 요소가 없어 아무 줄도 고르지 않습니다.

3. 플로우 카페 주문 칸입니다. 처음부터 체크된 '샷 추가'에 맞춰 무엇이 굵게 그려질까요? 굵어지는 요소를 모두 고르세요. 기호를 다 쓰세요.

<label for="shot">샷 추가</label>
<input
  id="shot"
  type="checkbox"
  checked
/>
<p>라테 4500원</p>
:checked + label {
  font-weight: bold;
}
  • ㉠ label 샷 추가
  • ㉡ p 라테 4500원
  • ㉢ 없음
힌트

체크된 칸 바로 뒤에 오는 형제가 무엇인지 나무에서 보세요.

정답

㉢ — 굵어지는 요소가 없습니다.

결과 화면
잰 값개발자 도구에서 본 값
  • label의 font-weight400
  • p의 font-weight400
  • :checked + label은 체크된 칸 바로 뒤의 label입니다. 이 칸 바로 뒤는 p이고, 짝 라벨은 칸 앞에 있습니다(브라우저 규칙 4).
  • ㉠을 고른 것은 for로 이은 짝이면 앞에 있어도 고른다고 본 것입니다. +는 짝이 아니라 나무의 차례로 바로 뒤 형제 하나를 봅니다.
  • ㉡을 고른 것은 + label을 '바로 뒤 아무 요소'로 본 것입니다. 바로 뒤 형제가 label이어야 고릅니다.

컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요. 2절 예제를 ul로 감싸고 style 요소로 한 파일에 담아 todo.html로 저장해 엽니다. 칸을 눌러 취소선을 본 뒤, CSS의 li:has(input:checked)⁠를 li input:checked로 바꾸면 어떻게 될지 먼저 적고, 저장하고 새로고침해 칸을 다시 눌러 맞춰 봅니다. 다시 :has()로 되돌려 줄이 그어지는지 봅니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

HTML·CSS 목차로