:checked는 지금 골라진 라디오 · 체크박스를 고르는 가상 클래스이고, :has()는 괄호 안 선택자에 맞는 요소를 안에 가진 요소를 고르는 가상 클래스입니다. 둘을 함께 쓰면 칸의 상태로 그 칸을 감싼 줄 전체를 바꿀 수 있습니다.
할 일 목록에서 체크한 줄에 취소선을 긋고 싶다고 합시다. 그런데 체크되는 것은 칸(input)이고, 취소선을 그을 곳은 그 칸을 감싼 줄(li)입니다. 지금까지의 선택자는 바깥에서 안쪽으로만 골랐습니다. 안에 든 것의 상태로 바깥 요소를 고르는 길이 :has()입니다. 이 레슨의 새 문법은 이 둘뿐입니다.
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-linenoneli: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로 바꿔 보세요. 제목의 색을 보세요.
<section>
<h2>오늘 할 일</h2>
<ul>
<li>
<input
type="checkbox"
aria-label="완료 표시"
checked
/>
우유 사기
</li>
</ul>
</section>(input:checked) {
color: #6e6e73;
}여기서 고친 코드는 저장하지도 보내지도 않습니다. 새로 열면 처음 코드입니다.
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-weight400p의font-weight400
:checked + label은 체크된 칸 바로 뒤의label입니다. 이 칸 바로 뒤는p이고, 짝 라벨은 칸 앞에 있습니다(브라우저 규칙 4).- ㉠을 고른 것은
for로 이은 짝이면 앞에 있어도 고른다고 본 것입니다.+는 짝이 아니라 나무의 차례로 바로 뒤 형제 하나를 봅니다. - ㉡을 고른 것은
+ label을 '바로 뒤 아무 요소'로 본 것입니다. 바로 뒤 형제가label이어야 고릅니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
2절 예제를 ul로 감싸고 style 요소로 한 파일에 담아 todo.html로 저장해 엽니다. 칸을 눌러 취소선을 본 뒤, CSS의 li:has(input:checked)를 li input:checked로 바꾸면 어떻게 될지 먼저 적고, 저장하고 새로고침해 칸을 다시 눌러 맞춰 봅니다. 다시 :has()로 되돌려 줄이 그어지는지 봅니다.