中級 jQuery ajax PHP JSON

ボタンを押すたびに、次の25件を読み込んで表示する

100件のお知らせを25件ずつ表示し、「次の25件」ボタンでページを切り替えずに続きを読み込みます。「もっと見る」ボタンの作り方です。データベースで使うときの LIMIT の書き方も紹介します。

このサンプルは2017〜2018年ごろに作成したものです。2026年に PHP 8.3 で動くように書き直し、さらにデータベースがなくても動く見本に作り変えました。データベースを使うときの書き方は、解説の最後に載せています。

DEMO 実際に動かしてみよう

全 100 件のうち、25 件を表示しています。

No.日付種類件名
1002026-10-31お知らせサンプルのお知らせ その100
992026-10-29キャンペーンサンプルのお知らせ その99
982026-10-27メンテナンスサンプルのお知らせ その98
972026-10-25イベントサンプルのお知らせ その97
962026-10-23お知らせサンプルのお知らせ その96
952026-10-21キャンペーンサンプルのお知らせ その95
942026-10-19メンテナンスサンプルのお知らせ その94
932026-10-17イベントサンプルのお知らせ その93
922026-10-15お知らせサンプルのお知らせ その92
912026-10-13キャンペーンサンプルのお知らせ その91
902026-10-11メンテナンスサンプルのお知らせ その90
892026-10-09イベントサンプルのお知らせ その89
882026-10-07お知らせサンプルのお知らせ その88
872026-10-05キャンペーンサンプルのお知らせ その87
862026-10-03メンテナンスサンプルのお知らせ その86
852026-10-01イベントサンプルのお知らせ その85
842026-09-29お知らせサンプルのお知らせ その84
832026-09-27キャンペーンサンプルのお知らせ その83
822026-09-25メンテナンスサンプルのお知らせ その82
812026-09-23イベントサンプルのお知らせ その81
802026-09-21お知らせサンプルのお知らせ その80
792026-09-19キャンペーンサンプルのお知らせ その79
782026-09-17メンテナンスサンプルのお知らせ その78
772026-09-15イベントサンプルのお知らせ その77
762026-09-13お知らせサンプルのお知らせ その76

解説コードを読んでみよう

このサンプルでわかること

  • 最初の25件だけを PHP で表示し、続きは ajax で読み込む方法
  • 「何件目から読むか」を送り、続きのデータを JSON で返す PHP の作り方
  • 残りがなくなったらボタンを消す方法

使っているファイル

  • index.phpデモのページ(最初の25件を表示)
  • more.phpajax で呼ばれ、続きの25件を JSON で返す
  • data.php見本のデータ(お知らせ100件)と、取り出す関数

1. データと、取り出す関数

見本のお知らせを100件作ります。get_page($offset) は「$offset 件目から25件」を取り出す関数です。array_slice() は、配列の一部を切り出す関数です。

data.php PHP
<?php
// 見本のデータ:お知らせ 100 件
// 本物のサービスでは、ここでデータベースから取り出します(解説の最後を参照)

const PER_PAGE = 25;   // 1回に読み込む件数

// お知らせを新しい順に 100 件作る
function get_notices(): array
{
    static $list = null;          // 一度作ったら使い回す
    if ($list !== null) {
        return $list;
    }
    $cats = ['お知らせ', 'イベント', 'メンテナンス', 'キャンペーン'];
    $last = new DateTimeImmutable('2026-10-31');
    $list = [];
    for ($i = 100; $i >= 1; $i--) {
        $list[] = [
            'id'    => $i,
            'date'  => $last->modify('-' . ((100 - $i) * 2) . ' days')->format('Y-m-d'),
            'cat'   => $cats[$i % 4],
            'title' => sprintf('サンプルのお知らせ その%d', $i),
        ];
    }
    return $list;
}

// $offset 件目から $limit 件を取り出す(SQL の LIMIT と同じ働き)
function get_page(int $offset, int $limit = PER_PAGE): array
{
    return array_slice(get_notices(), $offset, $limit);
}

// 全部の件数
function count_notices(): int
{
    return count(get_notices());
}

function h($s): string
{
    return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8');
}

2. 最初の25件を PHP で表示する

ページを開いた時点で、最初の25件は PHP が表にして送ります。ボタンには「次は何件目から読むか」を data-next に書いておきます。

index.php(140〜150行目) PHP
<table class="table table-striped notice-table">
  <thead><tr><th>No.</th><th>日付</th><th>種類</th><th>件名</th></tr></thead>
  <tbody id="rows">
<?php foreach ($first as $row): ?>
    <tr><td><?= h($row['id']) ?></td><td><?= h($row['date']) ?></td><td><?= h($row['cat']) ?></td><td><?= h($row['title']) ?></td></tr>
<?php endforeach; ?>
  </tbody>
</table>

<p class="more-wrap">
  <button type="button" class="btn btn-primary" id="more" data-next="<?= count($first) ?>">次の25件を読み込む</button>

3. ボタンを押したら、続きを読み込む

$.getJSON() で more.php を呼び、offset(何件目から)を送ります。返ってきたデータを1件ずつ <tr> にして、表の最後に付け足します。

index.php(156〜184行目) PHP
  $("#more").on("click", function(){
    var $btn = $(this);
    $btn.prop("disabled", true).text("読み込み中…");

    // more.php に「何件目から」を送り、続きの25件を JSON で受け取る
    $.getJSON("more.php", { offset: $btn.data("next") })
      .done(function(res){
        $.each(res.items, function(i, row){
          $("<tr>").append(
            $("<td>").text(row.id),
            $("<td>").text(row.date),
            $("<td>").text(row.cat),
            $("<td>").text(row.title)
          ).appendTo("#rows");
        });
        $("#shown").text($("#rows tr").length);

        if (res.next === null) {            // 残りがなければボタンを消す
          $btn.prop("hidden", true);
          $("#end").prop("hidden", false);
        } else {                            // 次に読む位置を覚えておく
          $btn.data("next", res.next).prop("disabled", false).text("次の25件を読み込む");
        }
      })
      .fail(function(){
        $btn.prop("disabled", false).text("もう一度読み込む");
      });
  });
});

読み込んでいるあいだはボタンを disabled(押せない状態)にしています。続けて何回も押されて、同じ25件が二重に付け足されるのを防ぐためです。

4. more.php で、続きの25件を返す

more.php PHP
<?php
// 「次の25件」を JSON で返す
require_once __DIR__ . '/data.php';
header('Content-Type: application/json; charset=UTF-8');

// 何件目から読むか(0 以上の整数でなければ 0 にする)
$offset = filter_var($_GET['offset'] ?? 0, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
if ($offset === false) {
    $offset = 0;
}

$items = get_page($offset);
$next  = $offset + count($items);

echo json_encode([
    'items' => $items,
    'next'  => $next < count_notices() ? $next : null,   // 残りがなければ null
], JSON_UNESCAPED_UNICODE);

次に読む位置(next)も一緒に返します。もう残りがなければ null を返し、JavaScript はそれを見てボタンを消します。

押した回数送る offset返ってくる件next
1回目2526〜50件目50
2回目5051〜75件目75
3回目7576〜100件目null(おしまい)

データベースを使うときは

このページは見本なので、データベースを使わずに動かしています。本物のサービスにするときは、データを取り出す関数の中身だけをデータベースの読み書きに取り替えます。画面の作り方(HTML と JavaScript)は同じままで使えます。

get_page() の中を SQL の LIMIT(何件)と OFFSET(何件目から)に置き換えます。数字は必ず bindValue() で整数として渡します。

data.php(データベース版の例) PHP
function get_page(int $offset, int $limit = PER_PAGE): array
{
    $stmt = db()->prepare('SELECT id, date, cat, title FROM notices ORDER BY id DESC LIMIT :limit OFFSET :offset');
    $stmt->bindValue(':limit',  $limit,  PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
↑