jQuery / Seletor / :nth-child() - Selecionar posições an+b

No jQuery, :nth-child(an+b) seleciona elementos irmãos nas posições que correspondem a uma fórmula, contando a partir de um em cada pai. p:nth-child(2) significa um p que é o segundo filho do tipo elemento, e não o segundo p. Use :nth-of-type(2) para o segundo irmão p ou .eq(1) para o segundo elemento de um conjunto já selecionado.

Contar a partir de um e incluir outras tags

A documentação de :nth-child() segue as regras CSS de posição entre irmãos. A contagem recomeça para cada pai e inclui outras tags. Espaços de texto e comentários não são elementos. Elementos ocultos continuam ocupando posições enquanto permanecem no documento; portanto, a ordem visível não basta.

Se um pai contém h2, p, p, p:nth-child(1) fica vazio e p:nth-child(2) seleciona o primeiro p. Ele não reúne os p antes de numerá-los. Restrinja o escopo a filhos diretos com >, como no exemplo.

Interpretar an+b e fórmulas comuns

Na definição CSS de an+b, a e b são inteiros, e n assume inteiros não negativos como 0, 1 e 2. Apenas posições positivas resultantes correspondem. Zero e posições negativas não selecionam nada. -n+3 produz 3, 2, 1, as três primeiras posições; não conta de trás para a frente a partir do fim.

Condição Posições Significado
2 2 Segundo elemento de cada pai
2n ou even 2, 4, 6, … Posições pares
2n+1 ou odd 1, 3, 5, … Posições ímpares
3n+1 1, 4, 7, … Cada terceira posição a partir de um
-n+3 1, 2, 3 Primeiras três posições

Colocar p antes da condição mantém apenas os p nessas posições. Se o quarto filho é span, p:nth-child(2n) o exclui, mas não o ignora para renumerar um p posterior como quarto.

Exemplo completo com tags misturadas

Salve todo o código em UTF-8 como nth-child-demo.html e abra no navegador. Não é necessário instalar nada, mas é preciso internet para carregar o arquivo oficial do jQuery pela rede de distribuição de conteúdo, ou CDN. O código usa 4.0.0; fórmulas, inserção, restauração e comparação foram executadas com 3.7.1 e 4.0.0.

A contém h2, p, p, span, p; B contém h2, p, p, p. O resultado lista os IDs dos p para comparar com as posições das tags.

<!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>

A fórmula inicial é 2. O segundo elemento de cada grupo é seu primeiro p: a1 e b1 ficam destacados.

a1, b1 | 2
  1. 2n produz a1, b1, b3 | 3. O quarto elemento de A é span e não satisfaz p.
  2. 2n+1 produz a2, a3, b2 | 3. O h2 inicial fica de fora.
  3. 3n+1 produz b3 | 1. As posições 1 e 4 de A são h2 e span; apenas a quarta de B é p.
  4. -n+3 produz a1, a2, b1, b2 | 4: os p entre os primeiros três filhos do tipo elemento de cada pai.
  5. Use Reset e depois Prepend notice to A. O div inserido no início torna h2 o segundo filho de A. Com fórmula 2, resta b1 | 1. Cliques repetidos não geram duplicatas.
  6. Reset remove somente o div acrescentado, restaura a fórmula 2 e consulta novamente. O conteúdo original e a1, b1 | 2 retornam.

Comparar :nth-of-type() e .eq()

Após Reset, execute este trecho no console de desenvolvimento. :nth-of-type() conta irmãos da mesma tag; .eq() conta o conjunto já selecionado a partir de zero.

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

A primeira linha retorna a1 e b1, segundos filhos do tipo elemento que são p. A segunda retorna a2 e b2, segundos p de cada pai. A última retorna apenas a2, no índice 1 do conjunto completo de p. Um índice negativo de .eq() conta a partir do fim do conjunto e difere de -n+3.

Documento dinâmico e resultados inesperados

Seleções não refletem automaticamente alterações do documento. Um conjunto com a1 e b1 salvo antes da inserção mantém esses elementos depois. Consulte novamente, como refresh, e remova os destaques antigos para refletir as posições atuais.

Se o resultado ficar vazio, verifique posições zero ou inexistentes, a tag real e títulos, avisos ou elementos ocultos intermediários. Em tabelas, examine também pais intermediários como tbody no modelo de objetos do documento, DOM. Se nada responder, confira primeiro o carregamento do jQuery em Network e os erros em Console.