WordPress Logo ChatGPT を使って WordPress の AJAX ページネーション

ChatGPT を利用して WordPress の AJAX ページネーションを実装した際の覚書です。

WordPress の AJAX ページネーションのコード

更新日:2023年07月25日

作成日:2022年07月18日

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

関連ページ

ChatGPT に質問(プロンプトの入力)

WordPress で投稿一覧とページネーションを表示して AJAX で投稿を取得するコードを検索してみたのですが、良いコードが見つからず、ChatGPT を利用して実装してみました。

ChatGPT に対する命令や質問(ChatGPT に送るチャット欄に入力する文章)のことをプロンプトと呼びますが、このプロンプトの内容が適切かどうかで取得する結果(コード)が変わってきます。

チャット欄で改行するには shift + return を押します。

最初は以下のようなプロンプトを入力しましたが期待するようなコードは得られませんでした。

以下の処理を行う WordPress のコードを書いてください。


# 処理

任意のテンプレートで投稿の一覧を表示

ページネーションを表示

ページネーションのリンクをクリックするとAJAXで投稿を取得して一覧を更新

以下がほぼ期待通りのコードが得られたプロンプトで、期待するコードの条件を追加しました。実際にはもっと良いプロンプトの書き方があると思います。

以下の処理を行う WordPress のコードを書いてください。


# 処理

カスタムループを使って投稿を表示

ページネーションを追加

ページネーションのリンクをクリックすると AjAX で投稿とページネーションを取得してコンテンツを更新


# 条件

テンプレートのコードはコンテンツとページネーション部分の処理を記述

JavaScript は別ファイルに作成して functions.php で wp_enqueue_scripts で登録

AJAX のリクエスト先の admin-ajax.php と nonce は wp_add_inline_script で出力

check_ajax_referer を使った nonce の検証を含める

以下が ChatGPT からの回答です。

以下は、WordPressで要求された処理を行うためのテンプレートとfunctions.phpに追加するコードの例です。

まず、テンプレートファイル(例:custom_template.php)に以下のコードを追加します:

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

最後に、AJAXリクエストを処理するためのJavaScriptファイル(custom-ajax.js)をテーマのjsフォルダに作成し、以下のコードを追加します:

functions.php の AJAX ハンドラでは送信されたページ番号を元にページネーションも生成する必要があるのですが、その記述がなかったので以下の追加のリクエストをしました。

custom_ajax_handler() では、$paged を使ってページネーションを生成しレスポンスに含めてください

申し訳ありません、$pagedを使ってページネーションを生成し、レスポンスに含めるコードを追加いたします。以下がfunctions.phpに更新されたコードです:

そして、JavaScriptファイル(custom-ajax.js)も更新され、レスポンスからページネーションを取得して適切な部分に表示するようにします:

これで、ページネーションがAjaxリクエストで取得され、レスポンスに含まれて更新されるはずです。

ChatGPT のコードを試す

ChatGPT のコードを試してみたところ、リンクの href 属性からページ番号が取得できていないので以下を入力しました。

page = $(this).attr('href').split('/').pop(); では値が取得できません

data-page 属性を使用する別のコードが表示されましたが、余計におかしくなってしまいました。

実際には、page = $(this).attr('href').split('/').pop(); を修正すれば動作します。

指示の出し方が悪かったのかもしれません。

また、AJAX のリクエスト先と nonce は wp_add_inline_script で出力するように指示をしたのですが、 wp_localize_script が使われています。ネットで検索しても AJAX の使い方としては wp_localize_script が主に使われているため、まだ ChatGPT に使用例が認識されていないのかもしれません(推測)。

wp_add_inline_script は WordPress 4.5 で導入された比較的に新しい関数ですが、ChatGPT に問い合わせれば使い方は教えてくれるので、wp_add_inline_script 自体の情報は持っています。

WordPress のページネーションで AJAX

以下は ChatGPT のコードを元にテンプレートファイルでサブループを使った投稿の一覧と paginate_links() を使ってページネーションを表示して、リンクをクリックすると AJAX でページの再読み込みなしに投稿を取得して表示する例です。

上記の ChatGPT のコードはページ番号の取得の部分を修正すれば動作するのですが、他の部分もカスタマイズしてみました。

大まかな仕組みとしてはページネーションのリンクをクリックすると、JavaScript でページ番号を取得して AJAX リクエストにページ番号を含めて送信し、PHP 側(AJAX ハンドラー)ではそのページ番号を元に投稿一覧とページネーションの HTML を AJAX レスポンスとして返します。

JavaScript 側でレスポンスを取得したら、投稿一覧とページネーションを更新します。

テンプレートファイル

以下はテンプレートファイルのコンテンツ(一覧表示)とページネーションの部分のコードです。

クエリのパラメータに post_status を追加し、posts_per_page を表示設定の1ページに表示する最大投稿数に変更しています。posts_per_page は任意の数値を指定できますが、AJAX ハンドラーと合わせます。

<div id="content">
  <!-- 投稿を表示するループ処理 -->
  <?php
  $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
  $args = array(
    'post_type' => 'post',
    // 公開されている投稿のみを対象にする(追加)
    'post_status' => 'publish',
    // 表示設定の1ページに表示する最大投稿数を指定(変更)
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
  );
  $custom_query = new WP_Query($args);
  if ($custom_query->have_posts()) :
    while ($custom_query->have_posts()) : $custom_query->the_post();
      echo '<div class="post">';
      the_title('<h2><a href="' . esc_url(get_permalink()) . '">', '</a></h2>');
      the_excerpt();
      echo '</div>';
    endwhile;
    wp_reset_postdata();
  else :
    echo '<p>No posts found.</p>';
  endif;
  ?>
</div>

<!-- ページネーション -->
<div id="pagination">
  <?php
  echo paginate_links(array(
    'total' => $custom_query->max_num_pages,
    'current' => $paged,
  ));
  ?>
</div>

functions.php

以下はカスタマイズした functions.php のコードです。

スクリプトの登録・読み込みでは wp_localize_script を wp_add_inline_script に変更しています。

AJAX ハンドラーではテンプレート同様、クエリのパラメータに post_status を追加し、posts_per_page を表示設定の1ページに表示する最大投稿数に変更しています。

また、ob_start() などの記述も変更しています(元のままでも動きます)。

functions.php
// スクリプトの登録・読み込み
function custom_enqueue_scripts() {
  wp_enqueue_script(
    'custom-ajax',
    get_template_directory_uri() . '/js/custom-ajax.js',
    array('jquery'),  // jQuery を使わない場合は空の配列 array() を指定
    // タイムスタンプに変更
    filemtime(get_theme_file_path('/js/custom-ajax.js')),
    true
  );

  // admin-ajax.php と nonce の出力(wp_add_inline_script で書き換え)
  wp_add_inline_script(
    'custom-ajax',
    'const customAjax = ' . json_encode(array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      'nonce' => wp_create_nonce('custom-nonce'),
    )),
    'before'
  );
}
add_action('wp_enqueue_scripts', 'custom_enqueue_scripts');

// AJAX リクエスト用の処理(AJAX ハンドラー)
function custom_ajax_handler() {
  // nonce の検証
  check_ajax_referer('custom-nonce', 'security');

  // ページ番号($_POST['page'] の代わりに filter_input を使用)
  $paged = filter_input( INPUT_POST, 'page', FILTER_VALIDATE_INT );
  $args = array(
    'post_type' => 'post',
    // 追加
    'post_status' => 'publish',
    // 変更
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
  );

  $custom_query = new WP_Query($args);

  // バッファリング開始
  ob_start();
  if ($custom_query->have_posts()) :
    while ($custom_query->have_posts()) : $custom_query->the_post();
      // 投稿のタイトルやコンテンツを表示
      echo '<div class="post">';
      the_title('<h2><a href="' . esc_url(get_permalink()) . '">', '</a></h2>');
      the_excerpt();
      echo '</div>';
    endwhile;
    wp_reset_postdata();
  else :
    echo '<p>No posts found.</p>';
  endif;
  // ループの出力を変数に代入
  $posts = ob_get_clean();

  // ページネーションを取得してレスポンスに含める
  ob_start();
  echo paginate_links(array(
    'total' => $custom_query->max_num_pages,
    'current' => $paged,
  ));
  $pagination = ob_get_clean();

  // レスポンスを返す(JSON 形式に変換して echo )
  echo json_encode(array(
    'content' => $posts,
    'pagination' => $pagination,
  ));

  wp_die();
}
add_action('wp_ajax_custom_ajax_handler', 'custom_ajax_handler');
add_action('wp_ajax_nopriv_custom_ajax_handler', 'custom_ajax_handler');
出力を関数にする

ループの出力とページネーションの出力は、テンプレートと functions.php で同じものなので、関数にまとめれば1箇所で管理できます。

以下はループの出力とページネーションの出力の関数を functions.php に定義する例です。

functions.php(スクリプトの登録・読み込みは省略)
// ループを出力しクエリオブジェクトを返す関数
function my_custom_loop($paged) {
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
  );
  $custom_query = new WP_Query($args);
  if ($custom_query->have_posts()) :
    while ($custom_query->have_posts()) : $custom_query->the_post();
      echo '<div class="post">';
      the_title('<h2><a href="' . esc_url(get_permalink()) . '">', '</a></h2>');
      the_excerpt();
      echo '</div>';
    endwhile;
    wp_reset_postdata();
  else :
    echo '<p>No posts found.</p>';
  endif;
  // クエリオブジェクトを返す
  return $custom_query;
}

// ページネーションを出力する関数
function my_paginate_links($total, $current) {
  echo paginate_links(array(
    'total' => $total,
    'current' => $current,
  ));
}

// Ajaxリクエスト用の処理(AJAX ハンドラー)
function custom_ajax_handler() {
  check_ajax_referer('custom-nonce', 'security');
  $paged = filter_input( INPUT_POST, 'page', FILTER_VALIDATE_INT );
  ob_start();
  // ループの出力
  $custom_query = my_custom_loop($paged);
  $posts = ob_get_clean();

  ob_start();
  // ページネーションの出力
  my_paginate_links( $custom_query->max_num_pages, $paged);
  $pagination = ob_get_clean();

  echo json_encode(array(
    'content' => $posts,
    'pagination' => $pagination,
  ));

  wp_die();
}
add_action('wp_ajax_custom_ajax_handler', 'custom_ajax_handler');
add_action('wp_ajax_nopriv_custom_ajax_handler', 'custom_ajax_handler');

テンプレートでは定義した関数を呼び出します。

<div id="content">
  <!-- 投稿を表示するループ処理 -->
  <?php
    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    $custom_query = my_custom_loop($paged);
  ?>
</div>
<!-- ページネーション -->
<div id="pagination">
  <?php
    my_paginate_links( $custom_query->max_num_pages, $paged);
  ?>
</div>

JavaScript (AJAX)

以下は JavaScriptファイル(custom-ajax.js)です。

パーマリンクの設定が「基本」の場合、ページネーションのリンクの href の値は http://localhost/wp/?paged=1 のようになります。

そのため、以下ではパーマリンクの設定が「基本」の場合でもページ番号を取得できるように、ページ番号を取得する関数を定義しています。

また、2ページ目の投稿一覧を表示している際に、「前へ」をクリックするとページ番号は null や admin-ajax.php(ページ番号を取得する関数を使用しない場合)になるので、その場合はページ番号を1になるようにしています。

js/custom-ajax.js
jQuery(document).ready(function ($) {
  let page = 1;

  // ページ番号を取得する関数
  function getPageNumberFromPermalink(permalink) {
    let pageNumber = null;
    // パーマリンクに含まれるページ番号を正規表現で抽出
    const regex = /\/page\/(\d+)\/?$|\?paged=(\d+)/;
    const matches = permalink.match(regex);
    if (matches && matches.length >= 2) {
      // 正規表現で抽出した部分を整数に変換
      pageNumber = parseInt(matches[1] || matches[2]);
    }
    return pageNumber;
  }

  // ページネーションのリンクをクリックした時の処理
  $("#pagination").on("click", "a", function (e) {
    e.preventDefault();

    // リンクの href からページ番号を取得
    page = getPageNumberFromPermalink($(this).attr("href"));
    // パーマリンクの設定が「基本」以外であれば以下でも OK
    //page = $(this).attr('href').split('/').filter(Boolean).pop();

    // 2ページ目で「前へ」とクリックした場合の対策
    if( !page || page === 'admin-ajax.php' ) page = 1;

    // Ajaxリクエストを実行
    $.ajax({
      url: customAjax.ajaxurl,
      type: "post",
      data: {
        action: "custom_ajax_handler",
        // AJAX ハンドラーの check_ajax_referer() の第2引数の値をキー名に
        security: customAjax.nonce,
        page: page,
      },
      success: function (response) {
        // レスポンスを JSON 形式からオブジェクトにパース
        const data = JSON.parse(response);
        // コンテンツを更新
        $("#content").html(data.content);
        // ページネーションを更新
        $("#pagination").html(data.pagination);
      },
      error: function () {
        // エラー時の処理
        console.log("Error occurred.");
      },
    });
  });
});

jQuery を使わない

以下は jQuery を使わず XMLHttpRequest で AJAX を実装する例です。

ページネーションのリンクをクリックした時の処理は、ページネーションのリンクの親要素に click イベントのリスナーを登録して、子要素のリンクで発生したイベントを処理しています(イベントの移譲)。

js/custom-ajax.js(XMLHttpRequest)
document.addEventListener("DOMContentLoaded", function () {
  let page = 1;

  // ページ番号を取得する関数
  function getPageNumberFromPermalink(permalink) {
    let pageNumber = null;
    // パーマリンクに含まれるページ番号を正規表現で抽出
    const regex = /\/page\/(\d+)\/?$|\?paged=(\d+)/;
    const matches = permalink.match(regex);
    if (matches && matches.length >= 2) {
      // 正規表現で抽出した部分を整数に変換
      pageNumber = parseInt(matches[1] || matches[2]);
    }
    return pageNumber;
  }

  // ページネーションの各リンクの親要素
  const pagination = document.getElementById("pagination");
  // 投稿を表示する要素
  const content = document.getElementById("content");

  if (pagination) {
    // ページネーションのリンクをクリックした時の処理
    pagination.addEventListener("click", function (e) {
      e.preventDefault();

      // リンクの href からページ番号を取得
      const href = e.target.getAttribute("href");
      page = getPageNumberFromPermalink(href);

      // 2ページ目で「前へ」とクリックした場合の対策(page が null の場合)
      if (!page) page = 1;

      // オブジェクト形式のデータ(送信する際に URLSearchParams で変換)
      const data = {
        action: "custom_ajax_handler",
        security: customAjax.nonce,
        page: page,
      };

      // Ajaxリクエストを実行
      const xhr = new XMLHttpRequest();

      // responseType プロパティに 'json' を設定
      xhr.responseType = "json";

      xhr.open("POST", customAjax.ajaxurl);

      xhr.addEventListener("readystatechange", () => {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            // response で JSON オブジェクトを取得
            const data = xhr.response;
            // コンテンツを更新
            content.innerHTML = data.content;
            // ページネーションを更新
            pagination.innerHTML = data.pagination;
          } else {
            // エラー時の処理
            console.log(`失敗: ${xhr.status} (${xhr.statusText})`);
          }
        }
      });
      xhr.send(new URLSearchParams(data));
    });
  }
});

関連ページ:AJAX XMLHttpRequest の基本的な使い方

スクリプトの登録で依存スクリプトに jquery を指定しない

jQuery を使わない場合はスクリプトの登録で wp_enqueue_script() の第3引数の依存スクリプトに空の配列 array() を指定します。

functions.php
// スクリプトの登録・読み込み
function custom_enqueue_scripts() {
  wp_enqueue_script(
    'custom-ajax',
    get_template_directory_uri() . '/js/custom-ajax.js',
    array() ,  // 空の配列 array() を指定
    filemtime(get_theme_file_path('/js/custom-ajax.js')),
    true
  );

  wp_add_inline_script(
    'custom-ajax',
    'const customAjax = ' . json_encode(array(
      'ajaxurl' => admin_url('admin-ajax.php'),
      'nonce' => wp_create_nonce('custom-nonce'),
    )),
    'before'
  );
}
add_action('wp_enqueue_scripts', 'custom_enqueue_scripts');

ChatGPT で jQuery を使わないコードを生成

jQuery のコードを Vanilla JavaScript に変換するような作業は ChatGPT にしてもらうと簡単です。但し、実際に動作するかは確認する必要があります。

以下の jQuery のコードを jQuery を使わずに vanilla JavaScript で書き換えてください。

 

jQuery(document).ready(function ($) {
     let page = 1;

    ・・・中略・・・

})

以下は ChatGPT に作成してもらった jQuery を使わないコードの例です。

document.addEventListener("DOMContentLoaded", function () {
  let page = 1;

  // ページ番号を取得する関数
  function getPageNumberFromPermalink(permalink) {
    let pageNumber = null;
    // パーマリンクに含まれるページ番号を正規表現で抽出
    const regex = /\/page\/(\d+)\/?$|\?paged=(\d+)/;
    const matches = permalink.match(regex);
    if (matches && matches.length >= 2) {
      // 正規表現で抽出した部分を整数に変換
      pageNumber = parseInt(matches[1] || matches[2]);
    }
    return pageNumber;
  }

  function updateContentAndPagination(data) {
    // コンテンツを更新
    document.getElementById("content").innerHTML = data.content;
    // ページネーションを更新
    document.getElementById("pagination").innerHTML = data.pagination;
  }

  function ajaxRequest(page) {
    // Ajaxリクエストを実行
    var xhr = new XMLHttpRequest();
    xhr.open("POST", customAjax.ajaxurl, true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        // レスポンスを JSON 形式からオブジェクトにパース
        const data = JSON.parse(xhr.responseText);
        updateContentAndPagination(data);
      } else if (xhr.status !== 200) {
        // エラー時の処理
        console.log("Error occurred.");
      }
    };
    // AJAX ハンドラーの check_ajax_referer() の第2引数の値をキー名に
    const params = "action=custom_ajax_handler&security=" + customAjax.nonce + "&page=" + page;
    xhr.send(params);
  }

  function handlePaginationClick(e) {
    e.preventDefault();

    // リンクの href からページ番号を取得
    const targetPage = getPageNumberFromPermalink(e.target.getAttribute("href"));

    // 2ページ目で「前へ」とクリックした場合の対策
    if (!targetPage || targetPage === "admin-ajax.php") {
      page = 1;
    } else {
      page = targetPage;
    }

    ajaxRequest(page);
  }

  // ページネーションのリンクをクリックした時の処理を設定
  document.getElementById("pagination").addEventListener("click", handlePaginationClick);

  // 初回のコンテンツ取得
  ajaxRequest(page);
});

「Regenerate Response」をクリックすると別のコードが表示されます。

以下は「Regenerate Response」をクリックして表示された2つ目のコードです。こちらも動作しました。

document.addEventListener("DOMContentLoaded", function() {
  let page = 1;

  // ページ番号を取得する関数
  function getPageNumberFromPermalink(permalink) {
    let pageNumber = null;
    // パーマリンクに含まれるページ番号を正規表現で抽出
    const regex = /\/page\/(\d+)\/?$|\?paged=(\d+)/;
    const matches = permalink.match(regex);
    if (matches && matches.length >= 2) {
      // 正規表現で抽出した部分を整数に変換
      pageNumber = parseInt(matches[1] || matches[2]);
    }
    return pageNumber;
  }

  // ページネーションのリンクをクリックした時の処理
  const pagination = document.getElementById("pagination");
  pagination.addEventListener("click", function(e) {
    e.preventDefault();

    // リンクの href からページ番号を取得
    const href = e.target.getAttribute("href");
    page = getPageNumberFromPermalink(href);
    // パーマリンクの設定が「基本」以外であれば以下でも OK
    //page = href.split('/').filter(Boolean).pop();

    // 2ページ目で「前へ」とクリックした場合の対策
    if (!page || page === 'admin-ajax.php') page = 1;
    // Ajaxリクエストを実行
    const xhr = new XMLHttpRequest();
    xhr.open("POST", customAjax.ajaxurl, true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.onload = function() {
      if (xhr.status === 200) {
        // レスポンスを JSON 形式からオブジェクトにパース
        const data = JSON.parse(xhr.responseText);
        // コンテンツを更新
        document.getElementById("content").innerHTML = data.content;
        // ページネーションを更新
        document.getElementById("pagination").innerHTML = data.pagination;
      } else {
        // エラー時の処理
        console.log("Error occurred.");
      }
    };
    xhr.onerror = function() {
      // エラー時の処理
      console.log("Error occurred.");
    };
    xhr.send("action=custom_ajax_handler&security=" + customAjax.nonce + "&page=" + page);
  });
});

スクロールアニメーションを追加

以下はページネーションのリンクをクリックして投稿一覧が更新される際に、一覧の先頭にスクロールするアニメーションを追加する例です。

AJAX で投稿を取得できたら、requestAnimationFrame() を使って投稿一覧の先頭にスムーススクロールします。

js/custom-ajax.js
document.addEventListener("DOMContentLoaded", function () {
  let page = 1;

  // ページ番号を取得する関数
  function getPageNumberFromPermalink(permalink) {
    let pageNumber = null;
    // パーマリンクに含まれるページ番号を正規表現で抽出
    const regex = /\/page\/(\d+)\/?$|\?paged=(\d+)/;
    const matches = permalink.match(regex);
    if (matches && matches.length >= 2) {
      // 正規表現で抽出した部分を整数に変換
      pageNumber = parseInt(matches[1] || matches[2]);
    }
    return pageNumber;
  }

  /* アニメーション */
  //アニメーション(スムーススクロール)の持続時間
  const duration = 300;
  //開始時刻を代入する変数(最初は未定義)
  let start;
  //スクロール先の Y 座標(ウィンドウの左上からの座標)
  let targetY;
  //現在の垂直(Y)方向のスクロール量(位置)
  let currentY;
  //イージング関数の定義
  const easeInQuad = (x) => {
    return x * x;
  };
  //スクロール先を調整する値(上部の固定メニューの高さなど必要に応じて)
  let offset = 80;
  //requestAnimationFrame() のコールバック関数
  const smoothScroll = (timestamp) => {
    if (start === undefined) {
      start = timestamp;
    }
    //経過時間
    const elapsed = start ? timestamp - start : 0;
    //進捗度を算出してイージングを適用
    const relativeProgress = easeInQuad(Math.min(1, elapsed / duration));
    //移動する量(targetY)に進捗度を適用して scrollTo のY座標へ指定する値を算出
    const scrollY = currentY + targetY * relativeProgress;
    //上記で算出した位置へスクロール(上部の固定メニューの高さ分を offset で調整)
    window.scrollTo(0, scrollY - offset);
    //進捗度が1未満の場合は自身を繰り返す
    if (relativeProgress < 1) {
      requestAnimationFrame(smoothScroll);
    }
  };

  // ページネーションの各リンクの親要素
  const pagination = document.getElementById("pagination");
  // 投稿を表示する要素
  const content = document.getElementById("content");

  if (pagination) {
    // ページネーションのリンクをクリックした時の処理
    pagination.addEventListener("click", function (e) {
      e.preventDefault();

      // リンクの href からページ番号を取得
      const href = e.target.getAttribute("href");
      page = getPageNumberFromPermalink(href);

      // 2ページ目で「前へ」とクリックした場合の対策(page が null の場合)
      if (!page) page = 1;

      // オブジェクト形式のデータ(送信する際に URLSearchParams で変換)
      const data = {
        action: "custom_ajax_handler",
        security: customAjax.nonce,
        page: page,
      };

      // Ajaxリクエストを実行
      const xhr = new XMLHttpRequest();

      // responseType プロパティに 'json' を設定
      xhr.responseType = "json";

      xhr.open("POST", customAjax.ajaxurl);

      xhr.addEventListener("readystatechange", () => {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            // response で JSON オブジェクトを取得
            const data = xhr.response;
            // コンテンツを更新
            content.innerHTML = data.content;
            // ページネーションを更新
            pagination.innerHTML = data.pagination;

            /* アニメーション */
            //開始時刻を初期化
            start = undefined;
            //対象(スクロール先)の要素の Y 座標(ウィンドウ座標)
            targetY = content.getBoundingClientRect().y;
            //現在の垂直方向にスクロールされている量
            currentY = window.scrollY;
            //関数を実行
            smoothScroll();
          } else {
            // エラー時の処理
            console.log(`失敗: ${xhr.status} (${xhr.statusText})`);
          }
        }
      });
      xhr.send(new URLSearchParams(data));
    });
  }
});