jQuery / Selector / :odd - 奇数インデックスの要素を選択するセレクター

jQueryの :odd は、選択した集合を0から数え、インデックスが1、3、5となる要素、つまり2番目・4番目・6番目を選びます。「奇数」という名前から見た目の1行目を想像すると、結果が逆に見えます。新しいコードでは通常のCSSセレクターで範囲を定め、.odd() で絞り込みます。

:odd の意味と .odd() への移行

:odd の公式文書ではjQuery 3.4から非推奨とされ、.odd() メソッドへの置き換えが案内されています。.odd()は3.5で追加され、引数を取らず、現在の集合の奇数インデックスだけを含む新しいjQueryオブジェクトを返します。非推奨と削除は異なります。この比較は3.7.1と4.0.0の両方で実行し、一致を確認しました。

$("#records > tbody > tr:odd") は $("#records > tbody > tr").odd() に置き換えられます。3.5より古い版にはこのメソッドがないため、アプリとプラグインの互換性を確認してから更新します。

式 数える基準 初期状態の結果
tbodyのtrに :odd または .odd() 選択したデータ行の集合、0始まり r2, r4
ヘッダーを含む全trに .odd() ヘッダーがインデックス0 r1, r3, r5
tbodyのtr:nth-child(odd) 各親の兄弟位置、1始まり r1, r3, r5

ヘッダー付き表の完全な実行例

以下の全コードをUTF-8の odd-demo.html として保存し、ブラウザーで開きます。パッケージのインストールは不要ですが、CDN(Content Delivery Network、コンテンツ配信ネットワーク)から公式配布ファイルを読み込むインターネット接続が必要です。例では4.0.0を使用します。

theadにヘッダー1行、tbodyにデータ5行があります。IDは行を識別する名前であり、インデックスは現在の文書順と選択範囲で決まります。ボタンで条件を切り替えます。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Odd indexes in table rows</title>
  <style>
    body { font: 16px/1.6 sans-serif; margin: 24px; }
    table { border-collapse: collapse; width: 100%; max-width: 600px; }
    th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
    tr.marked { background: #fff0b3; }
    button { margin: 4px; padding: 8px; }
  </style>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
</head>
<body>
  <h1>Odd indexes in table rows</h1>
  <table id="records">
    <thead><tr id="heading"><th>Item</th><th>Amount</th></tr></thead>
    <tbody>
      <tr id="r1" class="eligible"><td>Item 1</td><td>10</td></tr>
      <tr id="r2"><td>Item 2</td><td>20</td></tr>
      <tr id="r3" class="eligible"><td>Item 3</td><td>30</td></tr>
      <tr id="r4" class="eligible"><td>Item 4</td><td>40</td></tr>
      <tr id="r5" class="eligible"><td>Item 5</td><td>50</td></tr>
    </tbody>
  </table>
  <button type="button" id="data">Data rows .odd()</button>
  <button type="button" id="all">Include header .odd()</button>
  <button type="button" id="css">Data :nth-child(odd)</button>
  <button type="button" id="prepend">Prepend one data row</button>
  <button type="button" id="reset">Reset</button>
  <p id="result" aria-live="polite"></p>
  <script>
    $(function () {
      function show($chosen) {
        $("#records tr").removeClass("marked");
        $chosen.addClass("marked");
        const ids = $chosen.map((_, row) => row.id).get().join(", ");
        $("#result").text(`${ids || "none"} | ${$chosen.length}`);
      }
      function dataRows() {
        show($("#records > tbody > tr").odd());
      }
      $("#data").on("click", dataRows);
      $("#all").on("click", () => show($("#records tr").odd()));
      $("#css").on("click", () => show($("#records > tbody > tr:nth-child(odd)")));
      $("#prepend").on("click", function () {
        if (!$("#extra").length) {
          $("#records > tbody").prepend('<tr id="extra"><td>Extra item</td><td>0</td></tr>');
        }
        dataRows();
      });
      $("#reset").on("click", function () {
        $("#extra").remove();
        dataRows();
      });
      dataRows();
    });
  </script>
</body>
</html>

初期状態はデータ行だけを選んで.odd()を適用するため、Item 2とItem 4が強調され、次の結果になります。

r2, r4 | 2

範囲変更・行追加・リセットを確認する

  1. Data rows .odd() はtbody直下のtrだけを数えます。インデックス1と3により r2, r4 | 2 となります。
  2. Include header .odd() はtheadのtrも含めます。ヘッダーが0を占めるため r1, r3, r5 | 3 に変わります。
  3. Data :nth-child(odd) はtbody内の1・3・5番目の兄弟を選び、r1, r3, r5 | 3 となります。前のボタンと同じ結果でも、数える規則は異なります。
  4. Prepend one data row はtbodyの先頭にExtra itemを挿入して.odd()を再計算します。元のr1がインデックス1へずれ、r1, r3, r5 | 3 となります。extraのID確認により、繰り返し押しても重複しません。
  5. Reset は追加行だけを削除し、データ行に.odd()を再適用します。元の5行と値を保ち、r2, r4 | 2 に戻ります。

showは古いmarkedクラスをすべて外してから新しい集合を強調するため、強調が蓄積しません。実際の表でもIDと直接の親子関係で範囲を限定し、別の表や入れ子の表を含めないようにします。

CSSの odd と比べる

開いた直後かReset後に、開発者ツールのConsoleで次の部分コードを実行します。最初の2行が従来の選択子と代替メソッド、最後がCSSの規則に従う :nth-child()です。

const ids = $set => $set.map((_, row) => row.id).get().join(", ");
console.log(ids($("#records > tbody > tr:odd")));
console.log(ids($("#records > tbody > tr").odd()));
console.log(ids($("#records > tbody > tr:nth-child(odd)")));
r2, r4
r2, r4
r1, r3, r5

.odd()は選択集合全体を一続きに数えますが、:nth-child(odd)は親ごとに兄弟位置を数え直します。tbodyが複数ならそれぞれ先頭から計算します。:nth-child()はタグの種類に関係なく要素の兄弟を数え、その位置でtr条件も満たすものを残します。空白テキストやコメントは要素の兄弟に数えません。

フィルター順序と動的な行の注意点

eligibleクラスはr1・r3・r4・r5にあります。$("#records > tbody > tr.eligible").odd() は先にこの4行を集め、r3とr5を残します。全5行に.odd()を適用してから .filter(".eligible") を付けると、r2とr4のうちr4だけが残ります。順序によってインデックスの基準集合が変わります。

空集合や1要素だけの集合にはインデックス1がないので、.odd()の結果も空です。選択結果は自動更新されません。挿入前に保存したr2・r4の集合は、その後も同じ要素を保持します。現在位置を反映したければdataRowsのように再検索します。

反応がなければNetworkのjQuery読み込み、Consoleのエラー、jQuery.fn.jquery を確認します。結果が逆ならヘッダーの包含、tbodyの範囲、フィルター順序、追加行の有無を調べます。見た目の行番号と0始まりのインデックスを分けて考えてください。