jQuery で自分自身の要素を取得する — $(this) の使い分けとアロー関数の罠
クリックされたボタンそのもの、ループで回っている今の要素。
jQuery でこれを指すのが $(this) です。
ただ、いまのコードは昔と前提が変わっています。
アロー関数で書くと $(this) は動きません。ここで止まる人が多いので、そこから書きます。
先に結論
| やりたいこと | 書き方 |
|---|---|
| 自分自身(jQuery オブジェクト) | $(this) |
| 自分自身(素の DOM 要素) | this / $(this).get(0) / $(this)[0] |
| アロー関数の中で自分自身 | $(e.currentTarget)($(this) は使えない) |
| 自分のテキスト | $(this).text() |
| 自分の中身の HTML | $(this).html() |
| 自分自身を含む HTML | this.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() など)はそのまま使えますが、
既存コードを上げるときに引っかかる点があります。
削除されたもの
- ユーティリティ関数が削除された……
$.isArray、$.isFunction、$.type、$.parseJSON、$.trim、$.now、$.isNumericなど。標準のArray.isArray()、typeof、JSON.parse()、String.prototype.trim()に置き換えます - 古いブラウザのサポートが外れた……IE 11 未満、iOS 11 未満、Firefox 65 未満、Android Browser、Edge Legacy
- focus / blur の発火順が標準に合わせられた
- slim ビルドから Deferred が外れた
上げ方
一度に直せない規模なら、次の順番が安全です。
- jQuery Migrate プラグインを入れる
本体は 3 系のまま。削除予定の API を呼んだ箇所がコンソールに警告として出ます - 警告を潰す
警告が出たところを標準の書き方に置き換えます。この段階では動作は変わりません - 警告がゼロになってから 4.0 に上げる
focus / blur の順序と slim ビルドの Deferred はここで確認します - 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 が速いのは変換部分だけで、確認の時間は変わりません。