跳到主要内容
稻草人
プロフィール

EP.02

💻Technology

jQuery時代のコードを保守するときのチェックリスト

jQueryで書かれた古いコードを安全に保守するための確認ポイント。

BY 稻, 草人

最近のフロントエンド開発では、ReactやVue、TypeScriptなどを使う案件が増えています。

一方、実際の保守現場では、何年も前に作られたjQueryのコードを修正する機会がまだ少なくありません。WordPressのテーマやプラグイン、社内システム、管理画面などを見ると、jQueryが現在も普通に動いているケースは多いです。

自分も既存システムを保守するときに、最初は「このクリック処理だけ直せばよい」と思っていたのに、実際には別のファイルにも同じイベントが登録されていたり、Ajaxで後から追加された要素だけ動かなかったりして、想像以上に調査に時間がかかったことがあります。

jQuery自体が悪いというより、長年の追加修正によって、処理の入口や依存関係が分かりにくくなっていることが問題です。

この記事では、jQuery時代のコードを保守するときに、自分が先に確認しておきたいポイントをチェックリストとしてまとめます。すべてをモダンJavaScriptへ書き換えることが目的ではありません。既存機能を壊さず、安全に原因を特定し、必要な範囲だけ修正するための確認項目です。

目次

まず確認するチェックリスト

時間がない場合は、最低限、次の項目だけでも確認しておくと安心です。

  • 読み込まれているjQueryのバージョンを確認した
  • jQueryが複数回読み込まれていないか確認した
  • 対象処理を登録しているファイルをすべて検索した
  • 同じイベントが二重登録されていないか確認した
  • 対象要素が最初から存在するか、後から追加されるか確認した
  • this が何を指しているか確認した
  • .attr() と .prop() を正しく使い分けているか確認した
  • Ajaxの成功時だけでなく、失敗時と通信中も確認した
  • .html() に外部入力をそのまま渡していないか確認した
  • 使用中のjQueryプラグインと初期化順を確認した
  • 修正前の動作と再現手順を記録した
  • PC、スマートフォン、主要ブラウザで確認した
  • コンソールエラーとNetworkタブを確認した
  • キャッシュを削除した状態でも確認した

ここから、各項目をもう少し詳しく説明します。

1. 最初にjQueryのバージョンと読込順を確認する

最初に見るべきなのは、処理の中身よりもjQuery本体の状態です。

ブラウザの開発者ツールで、次のコードを実行すると、現在読み込まれているバージョンを確認できます。

jQuery.fn.jquery;

古いシステムでは、共通ヘッダー、個別画面、外部プラグインなどから、それぞれjQueryを読み込んでいる場合があります。

<script src="/js/jquery-1.12.4.min.js"></script>
<script src="/plugin/example.js"></script>
<script src="/js/jquery-3.7.1.min.js"></script>

このような順番になっていると、最初のjQueryに登録されたプラグインが、後から読み込まれたjQueryでは使えなくなる可能性があります。「プラグインのファイルは読み込まれているのに、$(...).pluginName is not a function が出る」という場合は、二重読込も疑ったほうがよいです。

確認するときは、HTMLだけではなく、テンプレート、共通レイアウト、WordPressのfunctions.php、タグ管理ツールなども見ます。

asyncとdeferにも注意する

スクリプトの読込順が保証されていないと、jQuery本体より先に依存ファイルが実行されることがあります。

<script async src="/js/jquery.min.js"></script>
<script async src="/js/app.js"></script>

asyncはダウンロード完了順に実行されるため、依存関係があるスクリプトには向いていません。deferを使う場合でも、HTMLに記載した順番と、WordPress側で付与された属性を含めて確認します。

2. 対象の処理を「画面」ではなく「コード全体」から探す

保守でよくあるのが、見つけた一つの処理だけを修正して、別ファイルにある同じ処理を見落とすことです。

例えば、保存ボタンのIDが#saveButtonなら、次のような文字列をプロジェクト全体から検索します。

#saveButton
saveButton
click
submit

HTML側のIDやクラスだけでなく、呼び出している関数名、AjaxのURL、表示されるメッセージなども検索すると見つけやすいです。

また、圧縮済みのJavaScriptだけを見て修正するのではなく、元のソースが別に存在しないか確認します。ビルド後のファイルを直接直すと、次回のビルドで変更が消える可能性があります。

3. DOMの準備ができる前に処理していないか確認する

jQueryコードでは、次の書き方をよく見ます。

$(function () {
  // DOMの準備後に実行する処理
});

対象要素がまだ生成されていない状態でイベントを登録すると、処理が動かない原因になります。

ただし、$(function () {})の中に入れれば、すべて解決するわけではありません。Ajaxやモーダル、タブ切替によって後から追加される要素は、DOMの初期読込時には存在しないためです。

ここでは、対象要素が次のどちらなのかを分けて考える必要があります。

  1. HTML読込時から存在する要素
  2. JavaScriptやAjaxによって後から追加される要素

4. 後から追加される要素にはイベント委譲を使う

最初から存在するボタンなら、次のコードでも動きます。

$('.delete-button').on('click', function () {
  // 削除処理
});

しかし、Ajaxの結果として後から追加された.delete-buttonには、このイベントは登録されません。

その場合は、最初から存在する親要素にイベントを登録します。

$('#resultArea').on('click', '.delete-button', function () {
  // 後から追加されたボタンでも動く
});

よく分からないからといって、すべて$(document)に登録すると、画面全体でイベント判定が行われ、どこから呼ばれた処理か分かりにくくなります。できるだけ対象に近く、最初から存在する親要素を指定したほうが保守しやすいです。

5. イベントが二重登録されていないか確認する

「一回クリックしただけなのにAjaxが二回送信される」「モーダルを開くたびに処理回数が増える」という現象は、イベントの二重登録が原因かもしれません。

function initModal() {
  $('#submitButton').on('click', function () {
    submitForm();
  });
}

initModal()がモーダルを開くたびに呼ばれると、クリックイベントが追加され続けます。

修正方法の一つは、イベントに名前空間を付け、一度解除してから登録することです。

function initModal() {
  $('#submitButton')
    .off('click.orderForm')
    .on('click.orderForm', function () {
      submitForm();
    });
}

ただし、無条件に.off('click')を使うと、同じ要素に登録されている他のクリック処理まで削除してしまいます。既存システムでは、名前空間を付けて対象を限定するほうが安全です。

一度だけ実行すればよい処理なら、.one()も使えます。

$('#confirmButton').one('click', function () {
  // 一度だけ実行
});

6. thisとアロー関数を混在させるときに注意する

jQueryのイベント処理では、通常の関数内のthisはイベントが発生した要素を指します。

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

これをそのままアロー関数に変えると、thisの意味が変わります。

$('.item').on('click', () => {
  $(this).addClass('is-active'); // 期待した要素を指さない
});

古いコードを整理するときに、見た目を統一するためだけに通常関数をアロー関数へ一括変換するのは危険です。イベント対象が必要なら、通常関数を残すか、event.currentTargetを使います。

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

7. .attr()と.prop()を使い分ける

チェックボックスやdisabledの状態を扱うときは、.attr()と.prop()の違いに注意します。

const checked = $('#agree').prop('checked');
$('#submitButton').prop('disabled', true);

checked、selected、disabledなど、現在の状態を取得・変更する場合は、基本的に.prop()を使用します。

一方、HTMLに書かれた属性値を取得したい場合は.attr()を使用します。

const href = $('#detailLink').attr('href');

画面上はチェックされているのに判定結果が合わない場合は、ここを確認します。

8. .data()とdata-*属性のずれを確認する

jQueryの.data()は便利ですが、一度読み込んだ値を内部に保持するため、HTML属性を後から変更した場合に値が一致しないことがあります。

<button id="target" data-status="ready">実行</button>
const first = $('#target').data('status');

$('#target').attr('data-status', 'done');

const second = $('#target').data('status');
// 状況によっては期待した最新値にならない

コード内の状態として管理するなら.data()で統一し、HTML属性そのものを読み書きするなら.attr('data-status')で統一したほうが混乱しにくいです。

9. Ajaxは成功時だけでなく、失敗時と通信中も見る

古いコードでは、成功した場合だけを想定している処理があります。

$.ajax({
  url: '/api/items',
  method: 'GET'
}).done(function (response) {
  renderItems(response);
});

この場合、サーバーエラーやタイムアウトが発生しても、利用者には何も表示されない可能性があります。

let request = null;

function loadItems() {
  if (request) {
    request.abort();
  }

  $('#loading').show();
  $('#errorMessage').hide();

  request = $.ajax({
    url: '/api/items',
    method: 'GET',
    dataType: 'json',
    timeout: 10000
  })
    .done(function (response) {
      renderItems(response);
    })
    .fail(function (xhr, textStatus) {
      if (textStatus !== 'abort') {
        $('#errorMessage').text('データの取得に失敗しました。').show();
        console.error('Ajax error:', xhr.status, textStatus);
      }
    })
    .always(function () {
      $('#loading').hide();
      request = null;
    });
}

Ajaxを確認するときは、次の点を見ます。

  • リクエストURLとHTTPメソッドは正しいか
  • 送信パラメータは想定どおりか
  • レスポンスのHTTPステータスは何か
  • JSONとして正しい形式か
  • タイムアウト時に画面が操作不能にならないか
  • ボタン連打で同じ処理が複数回送信されないか
  • 古いリクエストの結果が新しい結果を上書きしないか
  • CSRF対策用のトークンが必要か

画面にエラーが出ていなくても、Networkタブを見ると、実際には404、403、500になっていることがあります。

10. 外部入力を.html()へ直接渡さない

次のようなコードは、レスポンスの内容によってはXSSの原因になります。

$('#message').html(response.message);

HTMLとして表示する必要がない文字列なら、.text()を使います。

$('#message').text(response.message);

テンプレート文字列でHTMLを組み立てる場合も、APIの値、URLパラメータ、入力フォームの値などをそのまま結合しないようにします。

const $name = $('<span>').addClass('user-name').text(user.name);
$('#userArea').empty().append($name);

「社内システムだから大丈夫」と考えず、値がどこから来たものなのかを確認することが大切です。

11. show()、hide()とCSSの責任範囲を確認する

jQueryの.show()や.hide()は便利ですが、要素にインラインスタイルが追加されます。

$('#menu').hide();
<div id="menu" style="display: none;">...</div>

レスポンシブ対応やCSS側のdisplay: flex、display: gridと組み合わせると、意図しない表示になることがあります。

状態をクラスとして表現できる場合は、クラスの追加・削除に統一したほうが確認しやすいです。

$('#menu').toggleClass('is-open');
#menu {
  display: none;
}

#menu.is-open {
  display: block;
}

ただし、既存コードが.slideToggle()などのjQueryアニメーションに依存している場合は、CSSだけを変更すると挙動が変わる可能性があります。修正範囲を決めてから統一します。

12. アニメーションキューの蓄積を確認する

マウス操作やスクロールに合わせて.fadeIn()、.slideDown()、.animate()を何度も呼び出すと、アニメーションがキューにたまり、操作をやめた後も動き続けることがあります。

$('#panel').stop(true, true).slideToggle(200);

.stop(true, true)は、たまっているアニメーションを止めて最終状態へ進めます。ただし、すべての場所に機械的に追加すると表示が不自然になることもあるため、連続操作で現象を再現してから使用します。

13. jQueryプラグインの依存関係を確認する

古い画面では、jQuery本体だけではなく、スライダー、モーダル、バリデーション、日付選択などのプラグインを利用していることがあります。

確認する内容は次のとおりです。

  • プラグイン名とバージョン
  • jQuery本体との対応バージョン
  • 初期化コードがどのファイルにあるか
  • 同じ要素を複数回初期化していないか
  • CSSや画像など、JavaScript以外の依存ファイル
  • すでに保守終了していないか
  • CDN停止時や読込失敗時の影響

プラグインの初期化前に、存在確認を入れる方法もあります。

if ($.fn.slick && $('.slider').length) {
  $('.slider').not('.slick-initialized').slick({
    arrows: true
  });
}

ただし、エラーを隠すためだけに存在確認を追加するのではなく、「なぜ読み込まれていないのか」も調査したほうがよいです。

14. WordPressでは$が使えない場合がある

WordPressに同梱されているjQueryは、他のライブラリとの競合を避けるため、noConflictモードで動作します。そのため、グローバルの$を前提にするとエラーになる場合があります。

jQuery(function ($) {
  $('.menu-button').on('click', function () {
    $('.global-menu').toggleClass('is-open');
  });
});

また、WordPressでは<script>タグをテーマへ直接書くより、wp_enqueue_script()で依存関係を指定したほうが安全です。

wp_enqueue_script(
    'theme-main',
    get_stylesheet_directory_uri() . '/assets/js/main.js',
    array('jquery'),
    '1.0.0',
    true
);

これにより、jQueryより後にmain.jsを読み込ませる意図が明確になります。キャッシュ対策としてバージョン番号を更新する場合も、どのファイルが配信されているか確認しやすくなります。

15. jQueryの更新は一気に行わない

古いjQueryを最新版へ変更すると、セキュリティやブラウザ対応の面でメリットがあります。ただし、本体だけを入れ替えて動作確認を終えるのは危険です。

古いイベントAPI、削除済みメソッド、プラグイン依存、Ajaxの挙動などが影響する可能性があります。特に古い1.xや2.xから新しいメジャーバージョンへ上げる場合は、段階的に更新したほうが原因を特定しやすいです。

jQuery Migrateは、非推奨または削除された機能の利用をコンソールへ表示し、移行時の調査を助けてくれます。ただし、Migrateを入れたことで動くようになったから完了ではありません。警告を確認してコードを修正し、最後にMigrateを外しても動くことを確認します。

また、現在のブラウザ対応範囲も先に決めます。利用者に古いブラウザが含まれる場合は、jQueryだけでなく、使用しているJavaScript構文やCSSも影響を受けます。

16. すぐにVanilla JavaScriptへ置き換えるべきか

小さな処理であれば、jQueryを使わずに書けることも多いです。

document.querySelector('.menu-button')
  ?.addEventListener('click', function () {
    document.querySelector('.global-menu')
      ?.classList.toggle('is-open');
  });

しかし、既存画面の一部だけを中途半端に置き換えると、jQueryとVanilla JavaScriptの処理が混在し、かえって分かりにくくなることがあります。

自分は、次のように考えて修正範囲を決めています。

  • 障害対応や小さな仕様変更:既存の書き方に合わせ、必要な範囲だけ直す
  • 独立した新機能:可能ならjQueryへの依存を増やさない
  • 大規模改修:依存関係とテスト範囲を整理してから段階的に置き換える
  • jQueryを一箇所消すために大きなリスクが出る場合:無理に消さない

「jQueryは古いから全部削除する」ではなく、保守コスト、影響範囲、テスト可能性を見て判断したほうが現実的です。

17. 修正前に再現手順と正常動作を残す

保守では、コードを直す前の記録が重要です。

最低限、次の内容を残します。

  • 対象URLと画面名
  • 操作手順
  • 期待する結果
  • 実際の結果
  • 発生ブラウザと端末
  • コンソールエラー
  • Networkタブのレスポンス
  • 必要なユーザー権限やテストデータ

可能なら、修正前の画面を動画やスクリーンショットで残します。古いシステムでは仕様書と実際の動作が一致しないこともあるため、現在の動作自体が重要な資料になります。

18. 修正後は「変更箇所」だけでなく周辺も確認する

例えば、ボタンのクリック処理を修正した場合、ボタンを一回押して成功しただけでは十分ではありません。

次のような操作も確認します。

  • 連続クリック
  • ダブルクリック
  • 入力値が空の場合
  • 最大文字数や特殊文字を入力した場合
  • 戻る、進む、再読込
  • モーダルを何度も開閉した場合
  • Ajax通信が遅い場合
  • サーバーからエラーが返った場合
  • 権限が異なるユーザーで操作した場合
  • PCとスマートフォンで操作した場合

特にイベントの二重登録やアニメーションキューは、一回の操作では見つからず、同じ操作を繰り返したときにだけ発生することがあります。

19. キャッシュを疑う

JavaScriptを修正したのに画面へ反映されない場合、コードを何度も変更する前にキャッシュを確認します。

  • ブラウザキャッシュ
  • WordPressのキャッシュプラグイン
  • CDNキャッシュ
  • サーバー側キャッシュ
  • Service Worker
  • プロキシや社内ネットワークのキャッシュ

開発者ツールのNetworkタブで「Disable cache」を有効にし、実際に取得されたJavaScriptの内容とレスポンスヘッダーを確認します。

WordPressの場合は、wp_enqueue_script()のバージョン値を更新する方法もあります。ただし、本番環境で毎回ランダム値や現在時刻を使うとキャッシュが効かなくなるため、リリース単位のバージョンにしたほうがよいです。

20. 最後に不要コードを削除するときも慎重に行う

使われていないように見える関数でも、HTML内の属性、別画面、外部プラグイン、動的な関数名などから呼ばれている可能性があります。

<button onclick="legacySubmit()">送信</button>

JavaScriptファイル内の呼出箇所だけを検索すると、このようなHTML側の呼出しを見落とします。

削除するときは、プロジェクト全体を検索し、対象画面だけでなく共通部品への影響も確認します。一度に大きく削除するより、変更を小さく分けてコミットしたほうが、問題が発生したときに原因を追いやすいです。

調査するときによく使う開発者ツール

最後に、自分がjQueryコードを調査するときによく見る場所をまとめます。

Console

  • JavaScriptエラー
  • jQuery Migrateの警告
  • console.log()での実行回数
  • jQueryとプラグインのバージョン
  • 対象要素の件数
console.log($('.delete-button').length);
console.log(jQuery.fn.jquery);

Network

  • JavaScriptファイルの読込順
  • 同じファイルの重複読込
  • AjaxのURL、メソッド、パラメータ
  • HTTPステータス
  • レスポンス内容
  • キャッシュの有無

Elements

  • 対象要素が実際に存在するか
  • IDが重複していないか
  • クラスやインラインスタイルの変化
  • disabled、checkedなどの状態
  • Ajax実行前後のDOM差分

Sources

  • 実際に配信されているJavaScript
  • ブレークポイント
  • イベントリスナー
  • 圧縮前ソースとSource Map

まとめ

jQuery時代のコードを保守するときに難しいのは、文法そのものよりも、長い期間で積み重なった依存関係を把握することだと思います。

画面上では一つのボタンに見えても、共通JavaScript、画面個別JavaScript、jQueryプラグイン、Ajax、WordPressの処理など、複数の場所が関係している可能性があります。

そのため、最初から大きく書き換えるのではなく、次の順番で進めると安全です。

  1. 現象と再現手順を整理する
  2. jQueryのバージョンと依存関係を確認する
  3. イベントとDOM生成のタイミングを確認する
  4. コンソールとNetworkタブで実際の動きを見る
  5. 修正範囲を小さくする
  6. 正常系だけでなく、連続操作や異常系も確認する

新規開発ではjQueryを選ばない場合でも、既存システムを安全に保守するためには、jQueryの基本動作を理解しておく価値があります。

「古いコードだから全部直す」ではなく、「どこまで直せば安全に目的を達成できるか」を判断することが、保守では一番大切だと感じています。

参考資料