jQuery / Selector / :nth-child() - 형제 요소 중 an+b번째 요소들을 선택하는 선택자
jQuery의 :nth-child(an+b)는 각 부모 안에서 요소 형제를 1부터 세어 수식에 맞는 위치의 요소를 선택합니다. p:nth-child(2)는 ‘두 번째 p’가 아니라 ‘두 번째 요소 자식이면서 p인 요소’입니다. p만 모아 두 번째를 원하면 :nth-of-type(2), 선택 결과 전체의 두 번째를 원하면 .eq(1)과 구분해서 사용합니다.
위치는 1부터, 형제는 태그와 관계없이 세기
:nth-child() 공식 문서는 CSS의 형제 위치 규칙을 따릅니다. 위치는 부모마다 새로 시작하며, 다른 태그의 요소도 셉니다. 공백 텍스트와 주석은 요소가 아니므로 위치에 포함하지 않습니다. 숨겨진 요소라도 문서 구조에 남아 있으면 위치를 차지하므로 화면에 보이는 순서만으로 판단하지 않습니다.
부모 안에 h2, p, p가 차례로 있으면 p:nth-child(1)은 비어 있고 p:nth-child(2)가 첫 p를 고릅니다. 이 조건은 먼저 p만 모은 뒤 순번을 매기는 동작이 아닙니다. 바로 아래 자식만 다룰 때는 예제처럼 >로 범위를 제한합니다.
an+b와 자주 쓰는 수식
CSS의 an+b 정의에서 a와 b는 정수이고, n에 0, 1, 2처럼 음이 아닌 정수를 넣어 얻는 양의 위치가 대상입니다. 0이나 음수 위치는 선택하지 않습니다. -n+3은 3, 2, 1을 만들므로 처음 세 요소 위치를 뜻하며, 음수 인덱스로 끝에서 세는 표현은 아닙니다.
| 조건 | 해당 요소 위치 | 의미 |
|---|---|---|
| 2 | 2 | 각 부모의 두 번째 요소 |
| 2n 또는 even | 2, 4, 6, … | 짝수 위치 |
| 2n+1 또는 odd | 1, 3, 5, … | 홀수 위치 |
| 3n+1 | 1, 4, 7, … | 1부터 세 칸마다 |
| -n+3 | 1, 2, 3 | 처음 세 요소 위치 |
앞에 p 조건이 붙으면 위 위치에 있는 요소 중 p인 것만 남습니다. 예를 들어 네 번째가 span이면 p:nth-child(2n)에는 포함하지 않지만, 그 span을 건너뛰어 뒤의 p를 네 번째로 다시 세지도 않습니다.
혼합 태그에서 수식을 바꾸는 전체 예제
아래 전체 코드를 UTF-8의 nth-child-demo.html로 저장하고 브라우저에서 엽니다. 공식 jQuery 파일을 CDN(Content Delivery Network, 콘텐츠 전송 네트워크)에서 불러올 인터넷 연결이 필요하며 별도 설치는 없습니다. 코드는 4.0.0을 사용하고, 예제의 수식·추가·초기화와 비교 코드는 3.7.1과 4.0.0에서 실행해 확인했습니다.
A의 요소 자식은 h2, p, p, span, p이고 B는 h2, p, p, p입니다. 각 p의 ID를 결과에 출력하므로 태그의 순서와 비교할 수 있습니다.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sibling positions and formulas</title>
<style>
body { font: 16px/1.6 sans-serif; margin: 24px; }
.group { border: 1px solid #aaa; padding: 12px; margin: 12px 0; }
.group > * { display: block; margin: 6px 0; padding: 6px; }
.group > .marked { background: #fff0b3; outline: 2px solid #b56b00; }
button, select { padding: 8px; margin: 4px; }
</style>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
</head>
<body>
<h1>Sibling positions and formulas</h1>
<div id="groups">
<section class="group" id="group-a">
<h2>A heading</h2>
<p id="a1">A paragraph 1</p>
<p id="a2">A paragraph 2</p>
<span>A note</span>
<p id="a3">A paragraph 3</p>
</section>
<section class="group" id="group-b">
<h2>B heading</h2>
<p id="b1">B paragraph 1</p>
<p id="b2">B paragraph 2</p>
<p id="b3">B paragraph 3</p>
</section>
</div>
<label for="rule">Formula:</label>
<select id="rule">
<option value="2">2</option>
<option value="2n">2n (even)</option>
<option value="2n+1">2n+1 (odd)</option>
<option value="3n+1">3n+1</option>
<option value="-n+3">-n+3</option>
</select>
<button type="button" id="prepend">Prepend notice to A</button>
<button type="button" id="reset">Reset</button>
<output id="result" aria-live="polite"></output>
<script>
$(function () {
function refresh() {
$("#groups > .group > p").removeClass("marked");
const rule = $("#rule").val();
const $chosen = $("#groups > .group > p:nth-child(" + rule + ")");
$chosen.addClass("marked");
const ids = $chosen.map((_, item) => item.id).get().join(", ");
$("#result").text(`${ids || "none"} | ${$chosen.length}`);
}
$("#rule").on("change", refresh);
$("#prepend").on("click", function () {
if (!$("#extra").length) {
$("#group-a").prepend('<div id="extra">Notice</div>');
}
refresh();
});
$("#reset").on("click", function () {
$("#extra").remove();
$("#rule").val("2");
refresh();
});
refresh();
});
</script>
</body>
</html>
초기 수식은 2입니다. 양쪽 모두 두 번째 요소는 첫 p이므로 a1과 b1이 강조됩니다.
a1, b1 | 2
- 수식을 2n으로 바꾸면
a1, b1, b3 | 3입니다. A의 네 번째는 span이라 p 조건에서 빠집니다. - 2n+1은
a2, a3, b2 | 3입니다. 첫 위치의 h2는 p가 아니므로 제외됩니다. - 3n+1은
b3 | 1입니다. A의 1·4번째는 h2와 span이고, B의 네 번째만 p입니다. - -n+3은
a1, a2, b1, b2 | 4입니다. 각 부모의 처음 세 요소 중 p를 고릅니다. - Reset 후 Prepend notice to A를 누르면 A 앞에 div가 생깁니다. 수식 2에서 A의 두 번째는 h2로 바뀌어
b1 | 1만 남습니다. 반복 클릭은 중복을 만들지 않습니다. - Reset은 추가 div만 제거하고 수식을 2로 돌린 뒤 재조회합니다. 원래 내용과
a1, b1 | 2가 복구됩니다.
:nth-of-type()·.eq()와 비교하기
Reset 상태에서 개발자 도구의 Console에 아래 부분 코드를 실행합니다. :nth-of-type()는 같은 태그의 형제끼리 위치를 세고, .eq()는 이미 선택된 집합을 0부터 셉니다.
const ids = $set => $set.map((_, item) => item.id).get().join(", ");
console.log(ids($("#groups > .group > p:nth-child(2)")));
console.log(ids($("#groups > .group > p:nth-of-type(2)")));
console.log(ids($("#groups > .group > p").eq(1)));
a1, b1
a2, b2
a2
첫 줄은 부모마다 두 번째 요소이면서 p인 a1·b1, 둘째 줄은 부모마다 두 번째 p인 a2·b2입니다. 마지막 줄은 전체 p 집합의 인덱스 1인 a2 하나뿐입니다. .eq()의 음수는 집합의 끝에서 세는 방식이므로 -n+3 수식과 혼동하지 않습니다.
동적 문서와 예상과 다른 결과 점검하기
선택 결과는 문서 변경을 자동으로 반영하지 않습니다. 추가 전 a1·b1을 저장한 집합은 추가 후에도 그 요소를 유지합니다. 현재 형제 위치에 맞는 결과가 필요하면 refresh처럼 선택을 다시 실행하고, 이전 강조도 제거합니다.
결과가 비면 0이나 존재하지 않는 위치를 요청했는지, 해당 위치의 태그가 p인지, 중간에 제목·안내·숨긴 요소가 있는지 확인합니다. 표는 실제 DOM(Document Object Model, 문서 객체 모델)의 tbody 같은 중간 부모도 확인해야 합니다. 예제가 반응하지 않으면 Network의 jQuery 로딩과 Console 오류를 먼저 확인합니다.




