JavaScript

脱jQueryは怖くない。jQuery卒業のためのバニラJS移行完全対応表

2026年9月13日

未だに現場で「$(document).ready」を見かけると、なんだかホッとしてしまう自分がいます。とはいえ、もうjQueryに頼らなくてもDOM操作なんて余裕で書ける時代です。今回は「jQuery やめる」「JavaScript 移行」で検索してきた方向けに、実務でよく使うjQueryのメソッドをバニラJSでどう書き換えるか、一つずつ対応表形式でまとめてみます。脱jQueryの一歩目として保存版にどうぞ。

なぜ今さら脱jQueryなのか

正直に言うと、jQueryが「悪」だとは全く思っていません。IE対応が必須だった時代、あれほどブラウザ間の差異を吸収してくれるライブラリはありませんでした。ただ、モダンブラウザがDOM操作系のAPIをかなり充実させた今、jQueryに頼る理由が薄くなっているのも事実です。

一番のモチベーションはやっぱりパフォーマンスです。jQuery本体の読み込みだけで数十KBのオーバーヘッドが発生しますし、ちょっとしたセレクタ操作のためだけにライブラリを丸ごと読み込むのは正直もったいない。ページ表示速度やCore Web Vitalsを気にする案件が増えている今、外せるライブラリは外しておきたいところです。

もう一つは保守性の問題。jQueryのコードは書きやすい反面、後から読んだときに「このイベントどこで発火してるんだっけ」となりがちです。バニラJSに寄せておくと、フレームワークへの移行やTypeScript化もスムーズになります。個人的には、案件のリプレイスタイミングで少しずつ脱jQueryを進めるのが現実的だと感じています。

jQuery やめる前に押さえておきたい考え方

いきなり全部書き換えようとすると必ず挫折します。まずは「jQueryが担っていた役割」を整理してから着手するのがコツです。

セレクタ取得はquerySelectorで完結する

jQueryの$()が担っていた要素取得は、document.querySelectordocument.querySelectorAllでほぼ代替できます。CSSセレクタの書き方はそのまま使えるので、ここは移行のハードルが一番低い部分です。

// jQuery
$('.item');

// バニラJS(単一要素)
document.querySelector('.item');

// バニラJS(複数要素・NodeList)
document.querySelectorAll('.item');

注意点は戻り値の違いです。jQueryオブジェクトは配列風のメソッドが最初から使えますが、querySelectorAllが返すNodeListはforEachこそ使えるものの、mapfilterは直接呼べません。配列メソッドを使いたい場合はArray.from()で変換してあげましょう。

const items = Array.from(document.querySelectorAll('.item'));
items.filter(el => el.dataset.active === 'true');

jQueryメソッド→バニラJS対応表【実務頻出編】

ここからが本題です。実際の案件でよく見かけるjQueryのメソッドを、使用頻度順にバニラJSでの書き方に置き換えていきます。

クラスの追加・削除・トグル

addClassremoveClassclassListプロパティにそのまま対応するメソッドがあります。むしろバニラJSのほうがメソッド名が直感的です。

// jQuery
$('.box').addClass('active');
$('.box').removeClass('active');
$('.box').toggleClass('active');
$('.box').hasClass('active');

// バニラJS
document.querySelector('.box').classList.add('active');
document.querySelector('.box').classList.remove('active');
document.querySelector('.box').classList.toggle('active');
document.querySelector('.box').classList.contains('active');

複数要素に一括で適用したい場合はforEachが必要になる点だけ注意してください。jQueryは暗黙的に全要素へ処理してくれますが、バニラJSではNodeListをループする必要があります。

document.querySelectorAll('.box').forEach(el => {
  el.classList.add('active');
});

イベントの登録

onメソッドの置き換えはaddEventListener一択です。ここはほぼ1対1で対応するので、機械的に置換していける部分になります。

// jQuery
$('.btn').on('click', function () {
  console.log('clicked');
});

// バニラJS
document.querySelector('.btn').addEventListener('click', () => {
  console.log('clicked');
});

jQueryのonが便利だったのはイベント委任(デリゲーション)を簡単に書けた点です。動的に追加される要素にもイベントを効かせたい場合、バニラJSでは親要素にリスナーを付けてevent.targetで判定する形になります。

// jQuery(デリゲーション)
$(document).on('click', '.dynamic-btn', function () {
  console.log('clicked');
});

// バニラJS(デリゲーション)
document.addEventListener('click', (event) => {
  if (event.target.matches('.dynamic-btn')) {
    console.log('clicked');
  }
});

matchesメソッドを使うと、後から追加されたDOM要素でもセレクタ判定ができるのでかなり重宝します。地味に知らない人が多い気がするので覚えておいて損はありません。

CSS操作・要素の表示非表示

css()styleプロパティで対応します。単発ならそのまま代入、複数まとめて設定したい場合はObject.assignが便利です。

// jQuery
$('.box').css('color', 'red');
$('.box').css({ color: 'red', fontSize: '16px' });

// バニラJS
document.querySelector('.box').style.color = 'red';
Object.assign(document.querySelector('.box').style, {
  color: 'red',
  fontSize: '16px',
});

show()hide()はCSSのdisplayプロパティを操作しているだけなので、素直に書き換えればOKです。ただ、jQueryは元のdisplay値(blockinline-blockかなど)を内部的に覚えていてshow()実行時に復元してくれる仕様なので、バニラJSで再現する場合はCSSクラスの付け外しに寄せたほうが安全です。

// バニラJS(クラスで管理する方式がおすすめ)
document.querySelector('.box').classList.add('is-hidden');
.is-hidden {
  display: none;
}

Ajax通信はfetchに一本化

$.ajax()$.get()は、モダンブラウザ標準のfetch APIで完全に置き換え可能です。Promiseベースなので、async/awaitと組み合わせるとjQuery時代よりむしろ読みやすくなります。

// jQuery
$.ajax({
  url: '/api/data',
  method: 'GET',
  success: function (data) {
    console.log(data);
  },
  error: function (err) {
    console.error(err);
  },
});

// バニラJS
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}
fetchData();

POST送信でJSONを送りたい場合はheadersとbodyの指定を忘れずに。ここは移行時にハマりがちなポイントなので、案件で移行するときは通信周りだけ先にテストを書いておくと安心です。

fetch('/api/data', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'test' }),
});

DOM要素の生成・追加

appendhtml()もよく使われるメソッドです。バニラJSではinsertAdjacentHTMLcreateElementを使い分けます。

// jQuery
$('.list').append('<li>item</li>');
$('.box').html('<p>content</p>');

// バニラJS
document.querySelector('.list').insertAdjacentHTML('beforeend', '<li>item</li>');
document.querySelector('.box').innerHTML = '<p>content</p>';

ユーザー入力を含む文字列をそのままinnerHTMLに突っ込むとXSSのリスクがあるのは、jQuery時代からずっと変わらない注意点です。動的な値を扱う場合はtextContentで挿入するか、createElementで要素を組み立ててappendChildする方が安全です。

const li = document.createElement('li');
li.textContent = userInput; // エスケープされて安全
document.querySelector('.list').appendChild(li);

アニメーション周りは代替ライブラリかCSSに寄せる

animate()fadeIn()/fadeOut()のようなアニメーション系メソッドは、正直バニラJSだけで完全再現しようとすると地味に手間がかかります。個人的なおすすめは以下の2パターンです。

一つはCSSのtransitionやアニメーションクラスの切り替えで済ませる方法。もう一つはWeb Animations API(element.animate())を使う方法です。後者はキーフレームをJavaScript側で細かく制御できるので、jQueryのanimate()に近い柔軟性があります。

document.querySelector('.box').animate(
  [{ opacity: 0 }, { opacity: 1 }],
  { duration: 300, fill: 'forwards' }
);

複雑な演出が必要な案件では、無理にバニラJSだけで頑張らずGSAPのような軽量アニメーションライブラリを検討するのも十分アリだと思います。jQueryを剥がす目的はあくまで「軽量化と保守性の向上」なので、目的と手段を取り違えないようにしたいところです。

移行を進めるときの現実的な進め方

対応表を眺めているだけだと「よし、全部書き換えるぞ」という気持ちになりがちですが、実案件でいきなり全置換するのはリスクが高い進め方です。

まずはページ単位・機能単位で影響範囲を区切って、テストしやすい箇所から着手するのが無難です。特にAjax通信周りはエラーハンドリングの挙動が変わりやすいので、移行後は必ず異常系(通信失敗時など)の動作も確認しておきましょう。私自身、移行作業中に成功時の処理だけ移してエラーハンドリングを移し忘れ、本番でエラーが握りつぶされていたことがあります。地味に見つけにくいバグなので、他人事だと思わず気をつけてください。

もう一つのコツは、移行完了までjQueryとバニラJSを共存させても構わないという割り切りです。「jQueryが1行でも残っていたら失敗」ではなく、段階的に依存度を下げていく方が現実的です。無理に一気にやろうとするとスケジュールを圧迫しますし、テストの見落としも増えます。

まとめ

今回はjQueryでよく使うメソッドを軸に、バニラJSでの書き換え方を対応表形式でまとめてみました。セレクタ取得やクラス操作、イベント登録あたりは機械的に置き換えられる部分が多く、移行の初手としては取り組みやすい領域です。一方でAjax通信やアニメーションは、単純な1対1変換では済まない部分もあるので、案件の要件と相談しながら進めるのが良いと思います。

脱jQueryを進める最大のメリットはやっぱり軽量化と、コードの見通しの良さです。今はquerySelectorfetchclassListのようにモダンブラウザ標準のAPIだけでかなりのことができるようになりました。IE対応というくびきから解放された今だからこそ、無理に全部を一度に置き換えようとせず、触る機会があった箇所から少しずつバニラJS化していくのが、実務的にも精神的にも一番続けやすいやり方だと感じています。この記事が、jQuery案件を今も抱えているエンジニアの方の移行の足がかりになれば幸いです。

【参考文献】

  • MDN Web Docs – Document.querySelector(): https://developer.mozilla.org/ja/docs/Web/API/Document/querySelector
  • MDN Web Docs – EventTarget.addEventListener(): https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
  • MDN Web Docs – Element: classList property: https://developer.mozilla.org/ja/docs/Web/API/Element/classList
  • MDN Web Docs – Fetch API: https://developer.mozilla.org/ja/docs/Web/API/Fetch_API
  • MDN Web Docs – Element.animate(): https://developer.mozilla.org/ja/docs/Web/API/Element/animate
  • You Might Not Need jQuery: https://youmightnotneedjquery.com/

Related Posts