jQuery / メソッド / .parent() - 直接の親要素を選択する
jQueryの.parent()は選択した各要素から一段だけ上の直接の親を取得し、新しいjQueryオブジェクトとして返します。.parent(".card")はその親が.cardに一致するときだけ結果に含め、一致する祖先を求めてさらに上へは進みません。複数要素が共有する親の重複は除かれます。
parent・parents・closestの範囲
.parent()の仕様でいう親はDOM(Document Object Model、文書オブジェクトモデル)ツリーで直接上にあるノードです。見た目を囲む箱やCSSの配置基準と同じとは限りません。$(".pick").parent().addClass("picked")のように結果へメソッドをつなげられます。元のボタンは移動・削除されません。
| メソッド | 範囲 | 選択子を使った結果 |
|---|---|---|
| .parent(".card") | 直接上の一段 | 直接の親が.cardなら含む |
| .parents(".card") | 親から上の祖先 | 一致する祖先 |
| .closest(".card") | 自身から上へ | 各出発要素から最初に一致した対象 |
一致する祖先をすべて得るなら.parents()、途中にラッパーがあっても最も近いカードを得るなら.closest()を使います。closestは自身も調べるため、カードで呼ぶとそのカードが返る場合があります。parentの引数は絞り込み用の選択子文字列であり、関数や段数を渡す場所ではありません。
入れ子のカードで比較する完全な例
全コードをUTF-8のparent-demo.htmlに保存し、ブラウザーで開きます。コンテンツ配信ネットワーク(CDN)の公式jQueryファイル4.0.0を読むインターネット接続が必要ですが、インストールは不要です。初期選択、ラッパー追加、カード追加、初期化と以下のConsole例を3.7.1・4.0.0で実行しました。
Alphaのボタン二つの直接の親は共通のalpha-bodyです。Betaのボタンはカードbetaの直下にあります。青緑の枠が探索結果です。buttonsは出発ボタン数、parentsは結果数、DOM buttonsは文書に残る実際のボタン数です。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Select immediate parents</title>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<style>
body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
button, select { font: inherit; margin: 4px; max-width: 100%; }
#board, .card, .body { padding: 12px; border: 1px solid #aab; margin: 10px 0; }
.picked { outline: 3px solid teal; outline-offset: -3px; }
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
</style>
</head>
<body>
<h1>Select one parent level</h1>
<label for="mode">Traversal:</label>
<select id="mode">
<option value="parent">parent()</option>
<option value="filtered">parent('.card')</option>
<option value="closest">closest('.card')</option>
<option value="ancestors">parents('section')</option>
</select>
<button type="button" id="wrap">Wrap Beta button</button>
<button type="button" id="add">Add Gamma</button>
<button type="button" id="reset">Reset</button>
<section id="board">
<article id="alpha" class="card">
Alpha card
<div id="alpha-body" class="body">
<button type="button" id="alpha-one" class="pick"><span>Alpha one</span></button>
<button type="button" id="alpha-two" class="pick">Alpha two</button>
</div>
</article>
<article id="beta" class="card">
Beta card
<button type="button" id="beta-one" class="pick">Beta one</button>
</article>
</section>
<pre id="result" aria-live="polite"></pre>
<script>
$(function () {
const initial = $("#board").html();
function report() {
const $buttons = $("#board .pick");
let $result;
switch ($("#mode").val()) {
case "filtered": $result = $buttons.parent(".card"); break;
case "closest": $result = $buttons.closest(".card"); break;
case "ancestors": $result = $buttons.parents("section"); break;
default: $result = $buttons.parent();
}
$("#board, #board *").removeClass("picked");
$result.addClass("picked");
const ids = $result.map(function () { return this.id; }).get();
$("#result").text(`buttons=${$buttons.length}\nparents=${$result.length}: ${ids.join(", ") || "(none)"}\nDOM buttons=${$("#board .pick").length}`);
}
$("#mode").on("change", report);
$("#wrap").on("click", function () {
if (!$("#beta-body").length) $("#beta-one").wrap('<div id="beta-body" class="body"></div>');
report();
});
$("#add").on("click", function () {
if (!$("#gamma").length) $("#board").append('<article id="gamma" class="card">Gamma card <button type="button" id="gamma-one" class="pick">Gamma one</button></article>');
report();
});
$("#reset").on("click", function () { $("#board").html(initial); $("#mode").val("parent"); report(); });
report();
});
</script>
</body>
</html>
初期状態はボタン3個に対し、共通親が一つにまとまるので親は2個です。
buttons=3
parents=2: alpha-body, beta
DOM buttons=3
- parent('.card')ではbetaだけです。Alphaの直接の親は.bodyなので除外され、その上のalphaには進みません。
- closest('.card')ではalpha・betaです。Alphaの中間.bodyを越えて最も近いカードを見つけます。
- parents('section')では共通祖先boardが一度だけ現れます。
- Wrap Beta buttonを押してparent()に戻るとalpha-body・beta-bodyになります。parent('.card')は0個です。ボタンは残り、直接上の構造だけが変わっています。
- Add Gammaは新しいカード直下にボタンを追加します。parent('.card')はgamma一つ、closest('.card')はカード3個です。繰り返しのラップ・追加は重複を防ぎます。
- Resetで元のHTMLとparent()モードに戻ります。条件を一つずつ変更して初期化すると比較しやすくなります。
report()は毎回ボタンを選び直します。const $parents = $(".pick").parent()として保持した結果が、後のDOM構造変更を自動反映するわけではありません。ラッパー追加や移動後、現在の関係が必要な時点で再探索します。
重複・空の結果とイベントの出発点
Reset後、開発者ツールのConsoleで次を実行します。空の選択のparentも長さ0のjQueryオブジェクトです。ノードを直接使う前に.lengthを確認します。htmlの親は通常要素でなくdocumentになる場合があり、すべての結果にtagNameなど要素専用プロパティがあると仮定しません。
const $buttons = $("#board .pick");
console.log(`buttons=${$buttons.length}; unique parents=${$buttons.parent().length}`);
console.log(`Alpha parent(.card)=${$("#alpha-one").parent(".card").length}`);
console.log(`Alpha closest(.card)=${$("#alpha-one").closest(".card").attr("id")}`);
console.log(`card closest itself=${$("#alpha").closest(".card").attr("id")}`);
console.log(`empty parent count=${$().parent().length}`);
console.log(`html parent is document=${$("html").parent().get(0) === document}`);
buttons=3; unique parents=2
Alpha parent(.card)=0
Alpha closest(.card)=alpha
card closest itself=alpha
empty parent count=0
html parent is document=true
ボタン内のspanをクリックするとevent.targetはspanです。一致したボタンを基準にするには、委譲イベントの通常関数内でthisを使います。同じページのConsoleで次を実行するとspanのクリックを発生させ、最後に自分の名前空間の処理だけを解除します。
$("#board").on("click.parentExample", ".pick", function (event) {
console.log(`parent=${$(this).parent().attr("id")}; target=${event.target.tagName.toLowerCase()}`);
});
$("#alpha-one span").trigger("click");
$("#board").off(".parentExample");
parent=alpha-body; target=span
対象がspanでも親はボタンのalpha-bodyです。$(event.target).parent()ならボタン自体になります。この使い方でアロー関数のthisはボタンを指さないため通常関数を使います。カードが目的なら、そのボタンで.closest(".card")を呼びます。
予想外の親が選ばれたとき
開発者ツールのElementsで実DOMを確認します。プラグインが中間divを追加すると.parent(".card")が空になる場合があります。.parent().parent()を無作為に増やす前に、直接の親が必要か最も近い一致祖先が必要かを決めます。
出発選択子が広いと無関係な親にも一括でスタイルが付きます。#board .pickのように範囲を絞り、結果の.lengthとidを見てから変更します。結果が空でもすぐ作成せず、実行時点で出発要素が存在するか確認します。例のDOM準備関数は初期HTMLの解析後に実行し、後から追加したボタンはreport()の再呼び出しで反映します。




