WordPress Logo ChatGPT を使って WordPress のコードを生成

OpenAI により開発された ChatGPT を使って WordPress のコードをリクエストしてみました。

更新日:2023年07月25日

作成日:2023年7月14日

以下は WordPress での AJAX の基本的な使い方などについての説明はありません。WordPress での AJAX の基本的な使い方については以下を御覧ください。

関連ページ

ChatGPT にコードを作成してもらう

「WordPress で AJAX を使った infinite scroll のコードを教えてください」と入力すると数秒で説明とコードが表示されました。

コードは見やすくシンタックスハイライトされ、コメントも付けられており、コードをコピーするボタンも用意されています。サイトに合わせてコードの修正は必要ですが、それなりに動作するコードでした。

入力欄の上に表示される Regenerate response をクリックすると、異なるコードが表示されます。

また、例えば「jQuery を使わずに」などの追加の指示を出せばコードを変更してくれます。

コードについての説明を求めればそれなりの説明が返ってきました。

今回は試していませんが、コードを入力してエラーチェックをリクエストすることもできるようです。

以下は ChatGPT を使って「WordPress で AJAX を使った infinite scroll のコード」を作成した例です。

ChatGPT による WordPress で AJAX を使った infinite scroll

入力欄に「WordPress で AJAX を使った infinite scroll のコードを教えてください」と入力して return キーを押すと、ほんの数秒で ChatGPT から以下の回答が返ってきました。

Sure! 以下は、WordPressでAJAXを使用して無限スクロール(infinite scroll)を実装するための基本的なコードの例です。

まず、functions.phpファイルに以下のコードを追加します。

function load_more_posts() {
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
      // ポストの表示コードをここに追加する
    endwhile;
  endif;

  wp_reset_postdata();

  die();
}

add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

上記のコードは、load_more_postsという関数を定義し、AJAXリクエストを処理するためにWordPressのwp_ajaxフックとwp_ajax_noprivフックにそれを追加しています。

次に、テーマのJavaScriptファイル(たとえば、theme.jsなど)に以下のコードを追加します。

jQuery(function($) {
  var page = 2;
  var loading = false;
  var endOfPosts = false;

  function loadPosts() {
    if (loading || endOfPosts) {
      return;
    }

    loading = true;

    $.ajax({
      url: ajaxurl,
      type: 'post',
      data: {
        action: 'load_more_posts',
        page: page
      },
      success: function(response) {
        if (response) {
          // レスポンスを解析し、ポストを追加するコードをここに追加する
          page++;
          loading = false;
        } else {
          endOfPosts = true;
        }
      }
    });
  }

  $(window).scroll(function() {
    if ($(window).scrollTop() + $(window).height() >= $(document).height() - 200) {
      loadPosts();
    }
  });
});

上記のコードは、loadPostsという関数を定義し、スクロールイベントを監視してスクロールが特定の位置に達したらloadPosts関数を呼び出します。loadPosts関数では、$.ajaxメソッドを使用してload_more_postsアクションにAJAXリクエストを送信します。サーバーからのレスポンスに応じてポストを追加し、ページ数を更新します。

最後に、テンプレートファイル(たとえば、index.phpなど)に以下のコードを追加します。

<div id="posts-container">
  <?php
  $args = array(
    'post_type' => 'post',
    'paged' => 1
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
      // ポストの表示コードをここに追加する
    endwhile;
  endif;

  wp_reset_postdata();
  ?>
</div>

<script src="<?php echo get_template_directory_uri(); ?>/js/theme.js"></script>

上記のコードは、ポストを表示するためのコンテナを作成し、初期ページのポストを表示します。

これで、WordPressでAJAXを使用して無限スクロールを実装する基本的なコードが完成しました。必要に応じて、ポストの表示コードやセレクタ、追加のオプションなどを調整してください。また、load_more_posts関数内でのポストの表示コードをカスタマイズすることもできます。

完全なコードではありませんが、表示されたコードにはコメントが入っており、そのとおりにコードを追加すれば良さそうです。

実際にはこの例の場合、ajaxurl の値を JavaScript に渡す必要があるのと、functions.php のクエリの条件に 'post_status' => 'publish' を追加しないと下書きの投稿がカウントされるので、表示される投稿がずれてしまいますが、これだけの内容を数秒で教えてくれれば文句は言えません。

もっと的確な指示を出していれば、もっと良いコードが得られたと思います。

ChatGPT のレスポンスをカスタマイズ

以下は ChatGPT のレスポンスのコードを使って実際に動作するように変更してみた例です。

script タグをテンプレートに記述したくなかったので、functions.php で wp_enqueue_scripts で JavaScript ファイルを読み込むようにし、その際に wp_add_inline_script() で ajaxurl の値を JavaScript に渡しています。また、nonce を設定して検証するようにしています。

追加や変更した部分にはコメントを入れてあります(その他は回答のコードのまま)。

また、以下ではコンテンツ部分は get_template_part('template-parts/content', get_post_type()) としてテンプレートパーツを読み込んでいますが、テンプレートパーツを作成していない場合は、the_title() や the_excerpt() などの投稿を表示するコードを記述します。

functions.php
// 追加
function my_enqueue_infinite_scroll_scripts() {
  // JavaScript の読み込み
  wp_enqueue_script(
    'ajax-infinite-scroll-script',
    get_theme_file_uri('/js/infinite-scroll.js'),
    array('jquery'),
    filemtime(get_theme_file_path('/js/infinite-scroll.js')),
    true
  );

  // JavaScript にリクエスト URL と nonce を渡す(以下を追加)
  wp_add_inline_script(
    'ajax-infinite-scroll-script',
    'const ajax_infinite_scroll_params = ' . json_encode(array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      'my_ajax_nonce' => wp_create_nonce('my-ajax-nonce'),
    )),
    'before'
  );
}
add_action('wp_enqueue_scripts', 'my_enqueue_infinite_scroll_scripts');


function load_more_posts() {
  // nonce の値の検証を追加
  check_ajax_referer('my-ajax-nonce');
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    // 追加
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
      // テンプレートパーツでループ部分を表示(追加)
      get_template_part('template-parts/content', get_post_type());
    endwhile;
  endif;

  wp_reset_postdata();
  // die() を wp_die() に変更(変更しなくても OK)
  wp_die();
}
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');
infinite-scroll.js
jQuery(function($) {
  var page = 2;
  var loading = false;
  var endOfPosts = false;

  function loadPosts() {
    if (loading || endOfPosts) {
      return;
    }

    loading = true;

    $.ajax({
      url: ajax_infinite_scroll_params.ajaxurl,
      type: 'post',
      data: {
        action: 'load_more_posts',
        page: page,
        // nonce を追加
        _ajax_nonce: ajax_infinite_scroll_params.my_ajax_nonce,
      },
      success: function(response) {
        if (response) {
          // 取得したレスポンスを append() で追加
          $('#posts-container').append(response);
          page++;
          loading = false;
        } else {
          endOfPosts = true;
        }
      }
    });
  }

  $(window).scroll(function() {
    if ($(window).scrollTop() + $(window).height() >= $(document).height() - 500) {
      loadPosts();
    }
  });
});
テンプレートに追加するコード
<div id="posts-container">
  <?php
  $args = array(
    'post_type' => 'post',
    'paged' => 1
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
      // テンプレートパーツでループ部分を表示(追加)
      get_template_part('template-parts/content', get_post_type());
    endwhile;
  endif;

  wp_reset_postdata();
  ?>
</div>

別のコードをリクエスト Regenerate response

入力欄の上辺りに表示される「Regenerate response」をクリックすると、別の回答が表示されます。

今度も数秒で以下の回答が表示されました。

こちらの気持ちを読んだかのように、今度のコードは JavaScript は wp_enqueue_scripts を使って読み込み、ajaxurl の値は script タグに出力されるコードになっています。

また、'post_status' => 'publish' もしっかり追加されていて、JavaScript ではコンテンツを追加する append() の記述や、追加読み込みのボタンまで追加されています。

テンプレートのコードはありませんが、JavaScript に追加されている追加読み込みのボタンを記述するなどの説明もちゃんとあります。

まず、functions.phpファイルに以下のコードを追加します。

// AJAXコールバック関数
function load_more_posts() {
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      // 投稿の表示コードをここに追加
      // 例: the_title(), the_content()など
    }
  }

  wp_reset_postdata();
  die();
}

// AJAXアクションの設定
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

// スクリプトのエンキュー
function load_custom_scripts() {
  wp_enqueue_script('infinite-scroll', get_template_directory_uri() . '/js/infinite-scroll.js', array('jquery'), '1.0', true);
  wp_localize_script('infinite-scroll', 'infiniteScroll', array(
    'ajaxurl' => admin_url('admin-ajax.php'),
    'noposts' => __('No more posts found', 'textdomain')
  ));
}
add_action('wp_enqueue_scripts', 'load_custom_scripts');

次に、テーマのディレクトリに「js/infinite-scroll.js」という名前のファイルを作成し、以下のコードを追加します。

jQuery(function($){
  var page = 2;
  var loading = false;
  var finished = false;

  function loadPosts() {
    if(loading || finished) {
      return;
    }

    loading = true;
    $('.load-more-button').addClass('loading');

    $.ajax({
      url: infiniteScroll.ajaxurl,
      type: 'post',
      data: {
        action: 'load_more_posts',
        page: page
      },
      success: function(response) {
        if (response) {
          $('.post-container').append(response);
          page++;
          loading = false;
          $('.load-more-button').removeClass('loading');
        } else {
          finished = true;
          $('.load-more-button').addClass('hidden');
        }
      }
    });
  }

  $('.load-more-button').on('click', function(e){
    e.preventDefault();
    loadPosts();
  });

  $(window).scroll(function() {
    if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
      loadPosts();
    }
  });
});

これで、テーマ内の特定のテンプレートファイルに、「post-container」というクラス名を持つ投稿のコンテナ要素を作成し、「load-more-button」というクラス名を持つボタン要素を作成することで、無限スクロールの実装ができます。

ボタンをクリックするか、ページの末尾にスクロールすると、新しい投稿がロードされます。

このコードはベースとなるものであり、テーマの要件に応じてカスタマイズする必要があります。また、必要に応じてスタイリングやエラーハンドリングの追加も行ってください。

注意:このコードはWordPressのテーマファイル内で使用することを前提としています。プラグインを使用する場合は、関数とスクリプトのエンキューの部分を適切に修正してください。

レスポンスをカスタマイズ

以下は、上記の ChatGPT からの回答のコードを実際に使えるようにカスタマイズした例です。

追加や変更した箇所にはコメントを入れてあります。

functions.php
function load_more_posts() {
  // nonce の値の検証を追加
  check_ajax_referer('my-ajax-nonce');
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      // テンプレートパーツでループ部分を表示(追加)
      get_template_part('template-parts/content', get_post_type());
      // または the_title() や the_excerpt() など
    }
  }

  wp_reset_postdata();
  die();
}
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

function load_custom_scripts() {
  wp_enqueue_script(
    'infinite-scroll',
    get_template_directory_uri() .
    '/js/infinite-scroll.js',
    array('jquery'),
    // ただのバージョン番号から filemtime() を使った記述に変更
    filemtime(get_theme_file_path('/js/infinite-scroll.js')),
    true
  );
  wp_localize_script(
    'infinite-scroll',
    'infiniteScroll',
    array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      // 使用しないので削除
      //'noposts' => __('No more posts found', 'textdomain'),
      // nonce を追加
      'my_ajax_nonce' => wp_create_nonce('my-ajax-nonce'),
    )
  );
}
add_action('wp_enqueue_scripts', 'load_custom_scripts');
infinite-scroll.js
jQuery(function($){
  var page = 2;
  var loading = false;
  var finished = false;

  function loadPosts() {
    if(loading || finished) {
      return;
    }

    loading = true;
    $('.load-more-button').addClass('loading');

    $.ajax({
      url: infiniteScroll.ajaxurl,
      type: 'post',
      data: {
        action: 'load_more_posts',
        page: page,
        // nonce を追加
        _ajax_nonce: infiniteScroll.my_ajax_nonce,
      },
      success: function(response) {
        if (response) {
          $('.post-container').append(response);
          page++;
          loading = false;
          $('.load-more-button').removeClass('loading');
        } else {
          finished = true;
          $('.load-more-button').addClass('hidden');
        }
      }
    });
  }

  $('.load-more-button').on('click', function(e){
    e.preventDefault();
    loadPosts();
  });

  $(window).scroll(function() {
    if ($(window).scrollTop() + $(window).height() >= $(document).height() - 200) {
      loadPosts();
    }
  });
});

テンプレート部分のコードはなかったので自分で作成する必要があります。前の回答のテンプレートのコードを利用できまが、コンテンツ部分の div 要素には id ではなくクラスを指定し、回答の説明にあるように追加読み込みのボタンを追加しています。

<div class="post-container">
  <?php
  $args = array(
    'post_type' => 'post',
    'paged' => 1
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
      get_template_part('template-parts/content', get_post_type());
      // または the_title() や the_excerpt() など
    endwhile;
  endif;

  wp_reset_postdata();
  ?>
</div>
<button class="load-more-button" type="button">Load More</button>

また、回答の説明の「必要に応じてスタイリングやエラーハンドリングの追加も行ってください」とあるように、全ての投稿が読み込まれた際にボタンを非表示にするにはスタイルを設定し、必要に応じてボタンに追加されるクラス .loading を使ってスタイルを指定することができます。

style.css
.load-more-button.hidden {
  display: none;
}

追加の指示 jQuery を使わないコード

入力欄に追加の指示(リクエスト)を指定することができます。

「jQuery を使わないコードを教えてください」とリクエストしたら、今回は少し時間がかかりましたが(と言っても1分もかからず)コードと説明が表示されました。

以下が ChatGPT からのレスポンスです。

もちろんです!以下に、WordPressでAJAXを使用して無限スクロールを実装するための、jQueryを使わないコード例を示します。

まず、functions.phpファイルに以下のコードを追加します。

// AJAXコールバック関数
function load_more_posts() {
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      // 投稿の表示コードをここに追加
      // 例: the_title(), the_content()など
    }
  }

  wp_reset_postdata();
  die();
}

// AJAXアクションの設定
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

// スクリプトのエンキュー
function load_custom_scripts() {
    wp_enqueue_script('infinite-scroll', get_template_directory_uri() . '/js/infinite-scroll.js', array(), '1.0', true);
    wp_localize_script('infinite-scroll', 'infiniteScroll', array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      'noposts' => __('No more posts found', 'textdomain')
    ));
}
add_action('wp_enqueue_scripts', 'load_custom_scripts');

次に、テーマのディレクトリに「js/infinite-scroll.js」という名前のファイルを作成し、以下のコードを追加します。

(function() {
  var page = 2;
  var loading = false;
  var finished = false;
  var loadMoreButton = document.querySelector('.load-more-button');
  var postContainer = document.querySelector('.post-container');

  function loadPosts() {
    if (loading || finished) {
      return;
    }

    loading = true;
    loadMoreButton.classList.add('loading');

    var xhr = new XMLHttpRequest();
    xhr.open('POST', infiniteScroll.ajaxurl, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        if (xhr.responseText) {
          postContainer.insertAdjacentHTML('beforeend', xhr.responseText);
          page++;
          loading = false;
          loadMoreButton.classList.remove('loading');
        } else {
          finished = true;
          loadMoreButton.classList.add('hidden');
        }
      }
    };
    xhr.send('action=load_more_posts&page=' + page);
  }

  loadMoreButton.addEventListener('click', function(e) {
    e.preventDefault();
    loadPosts();
  });

  window.addEventListener('scroll', function() {
    if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight - 100) {
      loadPosts();
    }
  });
})();

レスポンスをカスタマイズ

前述の例同様、レスポンスのコードに nonce の追加と若干の変更をして動作するように書き換えた例です。変更部分にはコメントを入れてあります。

functions.php
function load_more_posts() {
  // nonce の値の検証を追加
  check_ajax_referer('my-ajax-nonce');
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      // テンプレートパーツでループ部分を表示(追加)
      get_template_part('template-parts/content', get_post_type());
      // または the_title() や the_excerpt() など
    }
  }

  wp_reset_postdata();
  die();
}
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

function load_custom_scripts() {
  wp_enqueue_script(
    'infinite-scroll',
    get_template_directory_uri() .'/js/infinite-scroll.js',
    array(),  // jQuery を使わないので空の配列になっている
    // ただのバージョン番号から filemtime() を使った記述に変更
    filemtime(get_theme_file_path('/js/infinite-scroll.js')),
    true
  );
  wp_localize_script(
    'infinite-scroll',
    'infiniteScroll',
    array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      // 使用しないので削除
      //'noposts' => __('No more posts found', 'textdomain'),
      // nonce を追加
      'my_ajax_nonce' => wp_create_nonce('my-ajax-nonce'),
    )
  );
}
add_action('wp_enqueue_scripts', 'load_custom_scripts');

指示した通り、JavaScript は jQuery は使わずに AJAX は XMLHttpRequest で記述され、グローバル変数を生成しないように即時関数で記述されています。

リクエストデータに nonce を追加し、window.pageYOffset を window.scrollY に変更しました。

infinite-scroll.js
(function() {
  var page = 2;
  var loading = false;
  var finished = false;
  var loadMoreButton = document.querySelector('.load-more-button');
  var postContainer = document.querySelector('.post-container');

  function loadPosts() {
    if (loading || finished) {
      return;
    }

    loading = true;
    loadMoreButton.classList.add('loading');

    var xhr = new XMLHttpRequest();
    xhr.open('POST', infiniteScroll.ajaxurl, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        if (xhr.responseText) {
          postContainer.insertAdjacentHTML('beforeend', xhr.responseText);
          page++;
          loading = false;
          loadMoreButton.classList.remove('loading');
        } else {
          finished = true;
          loadMoreButton.classList.add('hidden');
        }
      }
    };
    // nonce を追加
    xhr.send('action=load_more_posts&page=' + page + '&_ajax_nonce=' + infiniteScroll.my_ajax_nonce);
  }

  loadMoreButton.addEventListener('click', function(e) {
    //e.preventDefault();  //削除可能
    loadPosts();
  });

  window.addEventListener('scroll', function() {
    // window.pageYOffset を window.scrollY
    if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 400) {
      loadPosts();
    }
  });
})();

テンプレート部分は前述のコードと同じなので省略します。

更に書換えの指示 Intersection Observer を使う

infinite scroll が scroll イベントを使っているので、Intersection Observer API を使うように「scroll イベントの代わりに Intersection Observer API を使ったコードでお願いします」とリクエストしてみました。

今度も1分もかからずにコードと説明が表示されました。

以下が ChatGPT からのレスポンスです。

もちろんです!以下に、WordPressでAJAXとIntersection Observer APIを使用して無限スクロールを実装するためのコード例を示します。

まず、functions.phpファイルに以下のコードを追加します。

// AJAXコールバック関数
function load_more_posts() {
  $paged = $_POST['page'];
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      // 投稿の表示コードをここに追加
      // 例: the_title(), the_content()など
    }
  }

  wp_reset_postdata();
  die();
}

// AJAXアクションの設定
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

// スクリプトのエンキュー
function load_custom_scripts() {
  wp_enqueue_script('infinite-scroll', get_template_directory_uri() . '/js/infinite-scroll.js', array(), '1.0', true);
  wp_localize_script('infinite-scroll', 'infiniteScroll', array(
    'ajaxurl' => admin_url('admin-ajax.php'),
    'noposts' => __('No more posts found', 'textdomain')
  ));
}
add_action('wp_enqueue_scripts', 'load_custom_scripts');

次に、テーマのディレクトリに「js/infinite-scroll.js」という名前のファイルを作成し、以下のコードを追加します。

(function() {
  var page = 2;
  var loading = false;
  var finished = false;
  var loadMoreButton = document.querySelector('.load-more-button');
  var postContainer = document.querySelector('.post-container');

  function loadPosts() {
    if (loading || finished) {
      return;
    }

    loading = true;
    loadMoreButton.classList.add('loading');

    var xhr = new XMLHttpRequest();
    xhr.open('POST', infiniteScroll.ajaxurl, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        if (xhr.responseText) {
          postContainer.insertAdjacentHTML('beforeend', xhr.responseText);
          page++;
          loading = false;
          loadMoreButton.classList.remove('loading');
        } else {
          finished = true;
          loadMoreButton.classList.add('hidden');
        }
      }
    };
    xhr.send('action=load_more_posts&page=' + page);
  }

  function handleIntersection(entries, observer) {
    entries.forEach(function(entry) {
      if (entry.isIntersecting) {
          loadPosts();
          observer.unobserve(entry.target);
      }
    });
  }

  var observer = new IntersectionObserver(handleIntersection, {
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  observer.observe(loadMoreButton);
})();

これで、テーマ内の特定のテンプレートファイルに、「post-container」というクラス名を持つ投稿のコンテナ要素を作成し、「load-more-button」というクラス名を持つボタン要素を作成することで、無限スクロールの実装ができます。

ボタンが表示されると同時に、Intersection Observerがボタンの表示状態を監視し、ボタンが画面に表示された場合に新しい投稿をロードします。

このコードはベースとなるものであり、テーマの要件に応じてカスタマイズする必要があります。また、必要に応じてスタイリングやエラーハンドリングの追加も行ってください。

注意:このコードはWordPressのテーマファイル内で使用することを前提としています。プラグインを使用する場合は、関数とスクリプトのエンキューの部分を適切に修正してください。

レスポンスをカスタマイズ

今回の回答の JavaScript では、ボタンのクリックイベントが抜けていました。

また、observer.unobserve() により、一度しか loadPosts() が呼び出されないため observer.unobserve() は削除する必要がありました。

以下が動作するように書き換えた JavaScript の例です。functions.php とテンプレート部分は前述の例と同じなので省略します。

infinite-scroll.js
(function() {
  var page = 2;
  var loading = false;
  var finished = false;
  var loadMoreButton = document.querySelector('.load-more-button');
  var postContainer = document.querySelector('.post-container');

  function loadPosts() {
    if (loading || finished) {
      return;
    }

    loading = true;
    loadMoreButton.classList.add('loading');

    var xhr = new XMLHttpRequest();
    xhr.open('POST', infiniteScroll.ajaxurl, true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        if (xhr.responseText) {
          postContainer.insertAdjacentHTML('beforeend', xhr.responseText);
          page++;
          loading = false;
          loadMoreButton.classList.remove('loading');
        } else {
          finished = true;
          loadMoreButton.classList.add('hidden');
        }
      }
    };
    // nonce をパラメータに追加
    xhr.send('action=load_more_posts&page=' + page + '&_ajax_nonce=' + infiniteScroll.my_ajax_nonce);
  }

  // ボタンのクリックイベントが抜けていたので追加
  loadMoreButton.addEventListener('click', function() {
    loadPosts();
  });

  function handleIntersection(entries, observer) {
    entries.forEach(function(entry) {
      if (entry.isIntersecting) {
          loadPosts();
          // 以下を削除
          // observer.unobserve(entry.target);
      }
    });
  }

  var observer = new IntersectionObserver(handleIntersection, {
    root: null,
    rootMargin: '0px',
    threshold: 0.1
  });

  observer.observe(loadMoreButton);
})();
修正したコード

上記でカスタマイズしたコードでは、最後の投稿が表示された際にボタンがまだ残っていたりするなどの問題があるので以下のようにさらにカスタマイズしてみました。

テンプレート
<?php
$args = array(
  'post_type' => 'post',
  'paged' => 1
);
$query = new WP_Query($args);
// ページ総数を取得
$max_pages = $query->max_num_pages;
?>
<!-- ページ総数を data-max-pages 属性に指定して JS で参照-->
<div class="post-container" data-max-pages="<?php echo $max_pages; ?>">
  <?php
  if ($query->have_posts()) :
    while ($query->have_posts()) : $query->the_post();
      the_title('<h2><a href="' . esc_url(get_permalink()) . '">', '</a></h2>');
      the_excerpt();
    endwhile;
  endif;
  wp_reset_postdata();
  ?>
</div>
<?php if ($max_pages > 1) : ?>
  <!-- ページ総数が1より大きい場合にボタンを表示 -->
  <button class="load-more-button" type="button">Load More</button>
<?php endif; ?>
functions.php
function load_more_posts() {
  // nonce の値を検証を追加
  check_ajax_referer('my-ajax-nonce');
  // 一応取得した値はエスケープ
  $paged = filter_input(INPUT_POST, 'page', FILTER_VALIDATE_INT);
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'paged' => $paged
  );

  $query = new WP_Query($args);

  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      the_title('<h2><a href="' . esc_url(get_permalink()) . '">', '</a></h2>');
      the_excerpt();
    }
  }
  wp_reset_postdata();
  // wp_die() に変更
  wp_die();
}
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');

function load_custom_scripts() {
  wp_enqueue_script(
    'infinite-scroll',
    get_template_directory_uri() . '/js/infinite-scroll.js',
    array(),
    filemtime(get_theme_file_path('/js/infinite-scroll.js')),
    true
  );
  // wp_localize_script() の代わりに wp_add_inline_script() を使用
  wp_add_inline_script(
    'infinite-scroll',
    'const infiniteScroll = ' . json_encode(array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      'my_ajax_nonce' => wp_create_nonce('my-ajax-nonce'),
    )),
    'before'
  );
}
add_action('wp_enqueue_scripts', 'load_custom_scripts');
infinite-scroll.js
(function () {
  let page = 2;
  let loading = false;
  let finished = false;
  const loadMoreButton = document.querySelector('.load-more-button');
  const postContainer = document.querySelector('.post-container');

  // ボタンとコンテナがあれば
  if(loadMoreButton && postContainer) {

    // カスタム data 属性からページ総数を取得
    const max_pages = parseInt(postContainer.dataset.maxPages);

    function loadPosts() {
      if (loading || finished) {
        return;
      }

      loading = true;
      // クラスの追加ではなくボタンのテキストを変更
      loadMoreButton.textContent = 'loading...';

      // データを定義
      const data = {
        action: 'load_more_posts',
        _ajax_nonce: infiniteScroll.my_ajax_nonce,
        page: page,
      };

      const xhr = new XMLHttpRequest();
      xhr.open('POST', infiniteScroll.ajaxurl, true);
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
          if (xhr.responseText) {
            postContainer.insertAdjacentHTML('beforeend', xhr.responseText);
            loading = false;
            // クラスの削除ではなくボタンのテキストを変更
            loadMoreButton.textContent = 'Load More';
            // ページ番号とページ総数が同じであればボタンを削除
            if (page === max_pages) {
              loadMoreButton.remove();
            }
            page++;
          } else {
            finished = true;
            // クラスの追加ではなくボタンを削除
            loadMoreButton.remove();
          }
        }
      };
      // データを URLSearchParams に変換して送信(ヘッダーは不要)
      xhr.send(new URLSearchParams(data));
    }

    loadMoreButton.addEventListener('click', function () {
      loadPosts();
    });

    function handleIntersection(entries) {
      entries.forEach(function (entry) {
        if (entry.isIntersecting) {
          loadPosts();
        }
      });
    }

    const observer = new IntersectionObserver(handleIntersection, {
      root: null,
      // rootMargin を変更
      rootMargin: '-30px',
      threshold: 0.1,
    });

    observer.observe(loadMoreButton);
  }
})();

同じ質問をしてみる

+ New chat をクリックして新しいチャットで、「WordPress で AJAX を使った infinite scroll のコードを教えてください」と最初と全く同じ質問(リクエスト)をしてみました。

全く同じ質問ですが、回答に表示されたコードと説明は異なります。以下が今回の ChatGPT からのレスポンスです。

WordPressでAJAXを使用した無限スクロールの実装方法を説明します。以下の手順に従ってください。

functions.phpファイルを開き、以下のコードを追加します。

// Infinite Scrollのための関数を作成
function wp_infinite_scroll_scripts() {
  // jQueryを登録
  wp_enqueue_script('jquery');

  // スクリプトファイルを登録
  wp_enqueue_script('infinite-scroll', get_template_directory_uri() . '/js/infinite-scroll.js', array('jquery'), '1.0', true);

  // スクリプトに渡すデータを設定
  $infinite_scroll_data = array(
    'ajax_url' => admin_url('admin-ajax.php'),
    'query_vars' => json_encode($GLOBALS['wp_query']->query)
  );
  wp_localize_script('infinite-scroll', 'infinite_scroll_data', $infinite_scroll_data);
  }
add_action('wp_enqueue_scripts', 'wp_infinite_scroll_scripts');

// ページを読み込むたびに呼び出される関数
function wp_infinite_scroll() {
  // クエリパラメータから取得
  $query_vars = json_decode(stripslashes($_POST['query_vars']), true);

  // クエリを作成
  $query = new WP_Query($query_vars);

  // ループ処理
  if ($query->have_posts()) {
      while ($query->have_posts()) {
        $query->the_post();
        // 投稿の表示
        get_template_part('content', get_post_format());
      }
  }

  // ポストデータのリセット
  wp_reset_postdata();

  // 終了処理
  die();
}
add_action('wp_ajax_wp_infinite_scroll', 'wp_infinite_scroll');
add_action('wp_ajax_nopriv_wp_infinite_scroll', 'wp_infinite_scroll');

テーマフォルダ内に"js"という名前のディレクトリを作成します。

"js"ディレクトリ内に、infinite-scroll.jsという名前のファイルを作成します。その中に以下のコードを追加します。

jQuery(document).ready(function($) {
  var loading = false;
  var finished = false;
  var page = 2; // 最初のページ数

  function load_posts() {
    if (loading || finished) {
      return;
    }

    loading = true;

    $.ajax({
      url: infinite_scroll_data.ajax_url,
      type: 'post',
      data: {
        action: 'wp_infinite_scroll',
        query_vars: infinite_scroll_data.query_vars,
        page: page
      },
      success: function(response) {
        if (response.trim() === '') {
            // レスポンスが空の場合、すべての投稿を読み込んだとみなして終了
            finished = true;
        } else {
            // 投稿を追加
            $('.infinite-scroll-container').append(response);
            page++;
        }
        loading = false;
      },
      error: function() {
        loading = false;
      }
    });
  }

  // スクロール時の処理
  $(window).scroll(function() {
    var position = $(window).scrollTop();
    var bottom = $(document).height() - $(window).height();

    // 画面下部にスクロールした場合、投稿を読み込む
    if (position >= bottom * 0.8) {
      load_posts();
    }
  });

  // 初回読み込み時に投稿を読み込む
  load_posts();
});

テーマのテンプレートファイル(例:index.php、archive.phpなど)で、無限スクロールを適用したい場所に次のようなHTMLコードを追加します。

<div class="infinite-scroll-container">
  <!-- 投稿が追加される要素 -->
  <?php
  // 投稿のループ処理
  if (have_posts()) {
    while (have_posts()) {
      the_post();
      // 投稿の表示
      get_template_part('content', get_post_format());
    }
  }
  ?>
</div>

今回表示されたコードはメインループでの infinite scroll でした。

そのままのコードでは動かなかったので以下のような修正をしました(変更部分にはコメントを入れてあります)。

function wp_infinite_scroll_scripts() {
  // jQueryを登録(不要なので削除。削除しなくても動く)
  //wp_enqueue_script('jquery');

  wp_enqueue_script('infinite-scroll', get_template_directory_uri() . '/js/infinite-scroll.js', array('jquery'), '1.0', true);

  $infinite_scroll_data = array(
    'ajax_url' => admin_url('admin-ajax.php'),
    // $GLOBALS['wp_query']->query を $GLOBALS['wp_query']->query_vars に修正
    'query_vars' => json_encode($GLOBALS['wp_query']->query_vars)
  );
  wp_localize_script('infinite-scroll', 'infinite_scroll_data', $infinite_scroll_data);
}
add_action('wp_enqueue_scripts', 'wp_infinite_scroll_scripts');

function wp_infinite_scroll() {
  $query_vars = json_decode(stripslashes($_POST['query_vars']), true);
  // 追加(以下を追加しないとページが進まない)
  $query_vars['paged'] = $_POST['page'] ;
  // 追加(以下を追加しないと下書きの投稿も対象になる)
  $query_vars['post_status'] = 'publish';

  $query = new WP_Query($query_vars);
  if ($query->have_posts()) {
    while ($query->have_posts()) {
      $query->the_post();
      // サイトに合わせてテンプレートファイルの位置を変更
      get_template_part('template-parts/content', get_post_format());
    }
  }
  wp_reset_postdata();
  die();
}
add_action('wp_ajax_wp_infinite_scroll', 'wp_infinite_scroll');
add_action('wp_ajax_nopriv_wp_infinite_scroll', 'wp_infinite_scroll');

AJAX 部分はそのままのコードで使えました。

jQuery(document).ready(function($) {
  var loading = false;
  var finished = false;
  var page = 2;

  function load_posts() {
    if (loading || finished) {
      return;
    }

    loading = true;

    $.ajax({
      url: infinite_scroll_data.ajax_url,
      type: 'post',
      data: {
        action: 'wp_infinite_scroll',
        query_vars: infinite_scroll_data.query_vars,
        page: page
      },
      success: function(response) {
        if (response.trim() === '') {
          finished = true;
        } else {
          $('.infinite-scroll-container').append(response);
          page++;
        }
        loading = false;
      },
      error: function() {
        loading = false;
      }
    });
  }

  $(window).scroll(function() {
    var position = $(window).scrollTop();
    var bottom = $(document).height() - $(window).height();
    if (position >= bottom * 0.8) {
      load_posts();
    }
  });

  load_posts();
});

テンプレート部分は、テンプレートパーツの読み込みをサイトの構成に合わせてディレクトリを変更しました。

<div class="infinite-scroll-container">
<?php
if (have_posts()) {
  while (have_posts()) {
    the_post();
    // サイトに合わせてテンプレートパーツの位置を変更
    get_template_part('template-parts/content', get_post_format());
  }
}
?>

詳細をリクエスト

「詳しい説明をお願いします」とコードの詳細をリクエストしてみました。

以下がレスポンスです。

もちろんです。詳細な説明を以下に示します。

まず、functions.phpファイルに追加するコードの解説です。

  1. wp_infinite_scroll_scripts関数では、必要なスクリプトとデータを登録しています。wp_enqueue_script関数を使用して、jQueryとinfinite-scroll.jsというスクリプトファイルを登録します(スクリプトファイルは後ほど作成します)。また、wp_localize_script関数を使用して、スクリプトに渡すデータを設定しています。このデータには、admin-ajax.phpファイルのURLと現在のクエリパラメータを含めます。
  2. wp_infinite_scroll関数は、実際に投稿を読み込むための処理を行います。POSTリクエストで送信されたクエリパラメータを取得し、WP_Queryクラスを使用して新しいクエリを作成します。その後、ループを使用して投稿を表示します。最後に、wp_reset_postdata関数を使用してポストデータをリセットし、die関数を使用して処理を終了します。
  3. add_action関数を使用して、wp_infinite_scroll関数がwp_ajax_wp_infinite_scrollおよびwp_ajax_nopriv_wp_infinite_scrollアクションフックで呼び出されるように設定しています。これにより、認証されたユーザーと非認証ユーザーの両方が無限スクロールを使用できるようになります。

次に、infinite-scroll.jsファイルのコードの解説です。

  1. load_posts関数は、投稿を読み込むための関数です。読み込み中であるかどうかや、すべての投稿を読み込んだかどうかを管理するためのフラグや変数を使用します。Ajaxリクエストを使用して、サーバーに対してwp_infinite_scrollアクションを実行するようにリクエストを送信します。データとして、アクション名とクエリパラメータ、および現在のページ番号を渡します。
  2. $(window).scroll関数は、ウィンドウのスクロールイベントを処理する関数です。ウィンドウの位置とドキュメントの高さから、スクロールが画面の80%以降に達した場合に投稿を読み込むようにしています。load_posts関数を呼び出して、投稿を読み込む処理を実行します。
  3. load_posts関数では、サーバーからのレスポンスを処理します。レスポンスが空の場合、すべての投稿が読み込まれたとみなし、finishedフラグを設定します。それ以外の場合は、返された投稿を表示し、ページ番号を増やします。最後に、loadingフラグをリセットします。

最後に、テーマファイルに追加するコードの解説です。

  1. infinite-scroll-containerというクラスを持つ要素を作成します。この要素は、新たに読み込まれた投稿が追加される場所です。
  2. have_posts関数とthe_post関数を使用して、最初のページの投稿を表示します。この部分は、通常のWordPressの投稿ループです。

これで、WordPressでAJAXを使用した無限スクロールが実装されました。スクロール時に追加の投稿が自動的に読み込まれます。必要に応じて、CSSスタイルを追加してスクロール時の表示を調整することもできます。

ChatGPT を使ってみて

コードの生成以外にも、関数の説明を求めたり、コードを入力してエラーチェックをリクエストするなど色々な使い方が考えられ、うまく使えばコードを書く作業がかなり効率的になりそうです。

また、適切なコードを得るには、指示の出し方(プロンプトの書き方)に習熟する必要がありそうです。

修正箇所を的確に伝えられれば、ある程度まで ChatGPT とのやり取りでコードを修正(カスタマイズ)することも可能なようですが、完全なコードを生成させるのに時間を費やすよりは、表示されたコードを利用して自分でコードを完成させた方が効率的かとも感じました。

最初から複雑な内容ではなく、段階的に質問していく方が効果的と言われています。

今回はたった1行の質問(プロンプト)でそれなりのコードを得ることができましたが、指定した条件が少なかったため、 Regenerate response で再度表示されるコードには内容的に差がありました。

時々英語で回答が返ってきましたが、「日本語でお願い」とリクエストすると日本語に翻訳してくれます。

注意点としては、回答が必ずしも正しいというわけではなく、また、質問の内容によっては適切なコードを得られない可能性もあります。

現時点では2021年9月までのデータが用いられているので、コードに関しても最新の関数などの使い方を反映したコードが得られるわけではない点も留意する必要があるかと思います。