jQuery で自分自身の要素を取得する — $(this) の使い分けとアロー関数の罠

クリックされたボタンそのもの、ループで回っている今の要素。 jQuery でこれを指すのが $(this) です。

ただ、いまのコードは昔と前提が変わっています。 アロー関数で書くと $(this) は動きません。ここで止まる人が多いので、そこから書きます。

先に結論

やりたいこと書き方
自分自身(jQuery オブジェクト)$(this)
自分自身(素の DOM 要素)this / $(this).get(0) / $(this)[0]
アロー関数の中で自分自身$(e.currentTarget)($(this) は使えない)
自分のテキスト$(this).text()
自分の中身の HTML$(this).html()
自分自身を含む HTMLthis.outerHTML
兄弟の中で何番目か$(this).index()

$(this) と this は別物

イベントハンドラやループのコールバックの中で、this には素の DOM 要素が入っています。 jQuery のメソッドを使いたいなら $() で包む必要があります。

$('.item').on('click', function () {
    console.log(this);            // <li class="item">…</li>(DOM 要素)
    console.log($(this));         // jQuery オブジェクト

    console.log(this.id);         // OK(DOM のプロパティ)
    console.log(this.text());     // エラー(DOM に text() は無い)
    console.log($(this).text());  // OK
});

逆に、jQuery オブジェクトから素の DOM 要素を取り出すときは .get(0) か [0] です。

const el = $('#foo').get(0);   // DOM 要素
const el2 = $('#foo')[0];      // 同じ
$('#foo').get();               // 全要素を素の配列で

.get() は引数なしだと全要素の配列、インデックスを渡すとその1つを返します。 負の数も使えて、.get(-1) で最後の要素が取れます。

アロー関数で $(this) が効かなくなる

ここが現在いちばん引っかかるところです。

なぜ効かないのか

アロー関数は this を自分で持ちません。 定義された場所の外側のスコープの this をそのまま引き継ぐ仕様なので、 jQuery が「この要素だよ」と渡しても、受け取る箱がありません。

エラーにならずに静かに失敗します。$(this) が $(window) や $(undefined) を指すだけなので、コンソールには何も出ません。 「クリックしても何も起きない」ときは、まずハンドラの書き方を見てください。

動かない

$('.item').on('click', () => {
    $(this).addClass('active');   // this は外側のまま
});

2つの対処

どちらでも直ります。

1. function で書く(従来どおり)

$('.item').on('click', function () {
    $(this).addClass('active');
});

2. アロー関数のまま、イベント引数から取る

$('.item').on('click', (e) => {
    $(e.currentTarget).addClass('active');
});

コードベース全体をアロー関数で統一しているなら2番、 jQuery のコードだけ触っているなら1番が素直です。 混在させると、どちらの書き方のときに this が使えるのか分からなくなるので、 ファイル単位ででも揃えておくと後が楽です。

.each() も同じです。アロー関数にすると this は使えませんが、 コールバックの第2引数に要素が渡ってくるので、そちらを使えば動きます。

$('.item').each((i, el) => {
    $(el).text(i + '番目');
});

target と currentTarget

イベント委譲を使うときに効いてくる違いです。

指すもの
e.target実際にクリックされた、一番内側の要素
e.currentTargetハンドラが登録されている要素(this と同じ)
<li class="item">テキスト <span class="tag">NEW</span></li>
$('#list').on('click', '.item', function (e) {
    e.target;          // span.tag をクリックしたなら span
    e.currentTarget;   // 常に li.item
    this;              // 常に li.item(currentTarget と同じ)
});

中に要素が入っているボタンで「たまに効かない」という不具合は、だいたい e.target を見ているのが原因です。 「このハンドラが付いている要素」が欲しいなら currentTarget を使ってください。

自分自身の中身を取る

.text() と .html() の違い

この2つは取得範囲の扱いが違います。複数の要素にマッチしたときの挙動が特に紛らわしいところです。

メソッド取れるもの複数要素にマッチしたとき
.text()タグを除いたテキスト全要素のテキストを連結
.html()中身の HTML(タグ込み)先頭の要素だけ

自分自身を含む HTML

.html() が返すのは中身であって、自分自身のタグは含みません。 自分を含めた HTML が欲しいときは、DOM のプロパティを直接見ます。

// <li class="item">テキスト</li>
$('.item').html();          // "テキスト"
$('.item').get(0).outerHTML; // "<li class=\"item\">テキスト</li>"

// ハンドラの中なら this がそのまま使える
$('.item').on('click', function () {
    console.log(this.outerHTML);
});

外部から受け取った文字列を .html() に渡すのは避けてください。 <script> を除去すれば済む話ではなく、<img onerror="…"> のような形でも実行されます。 表示するだけなら .text() を使います。

位置・親・兄弟を取る

よく使うメソッド

$('.item').on('click', function () {
    $(this).index();                  // 兄弟の中で何番目か(0 始まり)
    $(this).closest('.card');         // 自分から上にたどって最初に見つかる .card
    $(this).parent();                 // 親(1つだけ)
    $(this).siblings();               // 自分以外の兄弟
    $(this).find('.tag');             // 自分の下から探す
    $(this).data('id');               // data-id の値
});

closest() と data() の落とし穴

.closest() は自分自身も対象に含みます。 $(this).closest('.item') は、自分が .item ならそのまま自分を返します。 ここは .parents()(自分を含まない)と挙動が違うところです。

.data() は一度読むと値をキャッシュします。 JavaScript 側から data- 属性を書き換えても .data() の戻り値は変わらないので、 属性そのものを読みたいときは .attr('data-id') を使ってください。

素の JavaScript で書くと

document.querySelectorAll('.item').forEach((el) => {
    el.addEventListener('click', (e) => {
        const self = e.currentTarget;

        self.textContent;                        // .text()
        self.innerHTML;                          // .html()
        self.outerHTML;                          // outerHTML
        self.closest('.card');                   // .closest()
        self.dataset.id;                         // .data('id')
        [...self.parentElement.children].indexOf(self);  // .index()
    });
});

closest() も dataset も標準で使えるので、 この用途だけのために jQuery を読み込む理由はもうありません。 すでに入っている既存コードを無理に外す必要はない、というだけの話です。

jQuery 4.0 で変わったこと

jQuery 4.0.0 が2026年1月にリリースされました。メジャーバージョンとしては約10年ぶりです。 ここまでの内容($(this)・.text()・.closest() など)はそのまま使えますが、 既存コードを上げるときに引っかかる点があります。

削除されたもの

上げ方

一度に直せない規模なら、次の順番が安全です。

  1. jQuery Migrate プラグインを入れる
    本体は 3 系のまま。削除予定の API を呼んだ箇所がコンソールに警告として出ます
  2. 警告を潰す
    警告が出たところを標準の書き方に置き換えます。この段階では動作は変わりません
  3. 警告がゼロになってから 4.0 に上げる
    focus / blur の順序と slim ビルドの Deferred はここで確認します
  4. Migrate を外す

AI に書き換えさせる

この手の置き換えは、AI に下書きさせるのが向いています。 対応表が機械的で、判断が要るところが限られているからです。

プロンプト

次の jQuery のコードを、素の JavaScript に書き換えてください。

条件:
- $(this) は e.currentTarget に置き換える。
  アロー関数の中では this を使わない
- .text() は textContent、.html() は innerHTML、
  .data('x') は dataset.x に対応させる
- イベント委譲($(parent).on('click', '.child', fn))は
  親で addEventListener して e.target.closest('.child') で絞る形にする
- jQuery 独自の挙動に依存していて、素の JS で等価にならない箇所は
  書き換えずに残し、その理由を最後に箇条書きで出す
- 動作が変わる可能性がある箇所にはコメントを入れる

--- コード ---
(ここに貼る)

効くのは下から2つ目です。 「等価にならないものは無理に書き換えず、理由を出せ」と先に言っておくと、 それっぽいが挙動の違うコードを返してくるのを抑えられます。

実際、等価にならない代表例がいくつかあります。 .text() が複数要素を連結するのに対して textContent は1要素だけ、 .data() がキャッシュするのに対して dataset は常に現在値、といったところです。 こういう差を AI は黙って潰しがちなので、先に釘を刺しておきます。

確認すること

見るところ確認の仕方
セレクタの一致範囲jQuery 独自セレクタ(:visible、:contains() など)が querySelectorAll に残っていないか。残っていれば実行時エラーになる
複数要素のとき要素が2つ以上あるケースで動かす。1つのときだけ動くコードになっていないか
後から追加される要素委譲が維持されているか。直接 addEventListener に変わっていると動かなくなる

書き換えは1ファイルずつ、動かして確認しながら進めてください。 まとめて投げると、どこで壊れたか分からなくなります。 AI が速いのは変換部分だけで、確認の時間は変わりません。

アーカイブ一覧に戻る