htmx 逆引きレシピ
定期ポーリングで更新するには?
公開日:
最終更新日:
「今の進捗どうなった?」や「監視画面を更新したい」を、ページ全体リロードなしで実現したいときは 定期ポーリングが便利です。
htmxなら hx-trigger="every 2s" のように書くだけで、一定間隔で最新HTMLを取りにいけます。
このページでは、進捗の定期更新・監視ダッシュボード・リアルタイム風の新着追加の3パターンを紹介します。
必要な部分だけを差し替えるので、軽く・分かりやすく運用できます。
使用するhtmx属性
hx-trigger:リクエストを発火するタイミングを指定(例:load, every 2s)hx-get:GETで更新用のHTMLを取りに行って差し替える(定期更新と相性が良い)hx-target:返ってきたHTMLを差し替える先の要素(CSSセレクタ)を指定hx-swap:差し替え方を指定(例:innerHTML/afterbegin)
利用シーン
- 「進捗/ステータスを定期更新」:バッチや非同期処理の進捗を、画面を更新せず追いかけたい
- 「監視ダッシュボード」:指標カードやログを一定間隔で更新して、異常に早く気づきたい
- 「手軽にリアルタイム風」:新着通知や更新履歴を、自然に増えていくUIで見せたい
デモ用CSS
CSS
<style>
.CARD{ padding: 1rem; border: 1px solid rgba(0,0,0,.12); border-radius: .8rem; background:#fff; }
.HR{ margin: 1.25rem 0; border: 0; border-top: 1px dashed rgba(0,0,0,.18); }
.HTMX-NOTE{ opacity: .78; font-size: .95rem; }
code{ background: rgba(0,0,0,.05); padding: .1rem .35rem; border-radius: .35rem; }
.POLL-TRIGGER{ display:none; }
.PROGRESS{
height: 12px;
border-radius: 999px;
background: rgba(0,0,0,.08);
overflow: hidden;
}
.PROGRESS__BAR{
height: 100%;
width: var(--P, 0%);
background: rgba(21,101,192,.85);
}
.TAG{
display:inline-block;
padding:.15rem .55rem;
border-radius:999px;
background:rgba(0,0,0,.06);
font-size:.85rem;
}
.GRID{
display:grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap:.75rem;
}
@media (max-width: 780px){
.GRID{ grid-template-columns: 1fr; }
}
table{ width:100%; border-collapse:collapse; }
th,td{ padding:.55rem .6rem; border-bottom:1px solid rgba(0,0,0,.08); vertical-align:top; }
th{ background: rgba(0,0,0,.03); text-align:left; white-space:nowrap; }
.FEED{
display:grid;
gap:.5rem;
max-height: 260px;
overflow:auto;
padding-right:.25rem;
}
.FEED li{
list-style:none;
border:1px solid rgba(0,0,0,.10);
border-radius:.75rem;
padding:.55rem .75rem;
background: rgba(0,0,0,.02);
}
</style>
① 進捗/ステータスを定期更新
バックグラウンド処理の進捗(%)や状態を、一定間隔で自動更新するデモです。
「完了したか分からない不安」を減らし、待ち時間をストレスなく見せられます。
HTML
<div class="DEMO">
<h4>① 進捗/ステータスを定期更新</h4>
<div
class="POLL-TRIGGER"
hx-get="/htmx/demo/_poll_progress.php"
hx-trigger="load, every 2s"
hx-target="#DEMO_POLL_1_RESULT"
hx-swap="innerHTML"
aria-hidden="true"
></div>
<div id="DEMO_POLL_1_RESULT" class="CARD">
<p class="HTMX-NOTE">ここに進捗が表示されます(自動更新)。</p>
</div>
</div>
PHP
<?php
// 型を厳密に扱う
declare(strict_types=1);
// セッションを開始する(進捗を保持する)
session_start();
// HTMLとして返す
header('Content-Type: text/html; charset=UTF-8');
// HTMLエスケープ関数を用意する
function h(string $s): string {
// 特殊文字をエスケープする
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
// 進捗データが無ければ初期化する
if (!isset($_SESSION['demo_poll_progress']) || !is_array($_SESSION['demo_poll_progress'])) {
// 初期の進捗状態を作る
$_SESSION['demo_poll_progress'] = [
'job_id' => 12001,
'percent' => 0,
'status' => 'RUNNING',
'message' => '処理を開始しました。',
'updated' => date('H:i:s'),
];
}
// 進捗データを取り出す
$st = (array)$_SESSION['demo_poll_progress'];
// 現在の進捗率を取り出す
$percent = (int)($st['percent'] ?? 0);
// 現在のステータスを取り出す
$status = (string)($st['status'] ?? 'RUNNING');
// 進捗が完了していたらリセットする
if ($percent >= 100) {
// 次のジョブIDに進める
$st['job_id'] = (int)($st['job_id'] ?? 0) + 1;
// 進捗をゼロに戻す
$st['percent'] = 0;
// ステータスを実行中に戻す
$st['status'] = 'RUNNING';
// メッセージを更新する
$st['message'] = '新しいジョブを開始しました。';
}
// 進捗を少し進める(ランダム)
$delta = random_int(3, 14);
// 進捗を増やす
$st['percent'] = min(100, (int)($st['percent'] ?? 0) + $delta);
// 進捗率を取り直す
$percent = (int)$st['percent'];
// ステータスを進捗に応じて変える
if ($percent >= 100) {
// 完了状態にする
$st['status'] = 'DONE';
// 完了メッセージにする
$st['message'] = '完了しました。';
} elseif ($percent >= 70) {
// 後半メッセージにする
$st['message'] = '最終処理中です。';
} elseif ($percent >= 30) {
// 中盤メッセージにする
$st['message'] = 'データを処理しています。';
} else {
// 前半メッセージにする
$st['message'] = '準備中です。';
}
// 更新時刻を入れる
$st['updated'] = date('H:i:s');
// セッションへ保存する
$_SESSION['demo_poll_progress'] = $st;
// 表示用の色ラベルを決める
$tag = $st['status'] === 'DONE' ? '完了' : '処理中';
// 進捗カードを描画する
echo '<div style="display:grid; gap:.6rem;">';
// タイトル行を描画する
echo '<div><strong>ジョブ #' . h((string)$st['job_id']) . '</strong> <span class="TAG">' . h($tag) . '</span></div>';
// メッセージを描画する
echo '<div class="HTMX-NOTE">' . h((string)$st['message']) . '</div>';
// 進捗率を描画する
echo '<div><strong>' . h((string)$percent) . '%</strong> <span class="HTMX-NOTE">(更新:' . h((string)$st['updated']) . ')</span></div>';
// 進捗バーを描画する
echo '<div class="PROGRESS"><div class="PROGRESS__BAR" style="--P:' . h((string)$percent) . '%;"></div></div>';
// 閉じる
echo '</div>';
デモ
① 進捗/ステータスを定期更新
ここに進捗が表示されます(自動更新)。
解説
-
hx-trigger="load, every 2s"で、ページ表示直後から2秒ごとに更新を行います。
ユーザー操作なしで自動更新できるため、進捗表示や監視画面に向いています。 -
hx-targetを進捗エリアに絞ることで、必要な範囲だけが差し替わるようになります。
画面全体を更新しないので、レイアウトの揺れが起きにくく体感も軽くなります。 -
サーバ側では進捗を保持し、毎回「今の状態」を返すだけにしています。
定期更新は“最後の状態を取りに行く”設計にすると、画面と状態がズレにくくなります。
② 監視ダッシュボード
キュー数・エラー数・応答時間などの指標と、直近ログをまとめて監視するデモです。
監視画面を開いているだけで最新状態になるので、異常の早期発見に役立ちます。
HTML
<div class="DEMO">
<h4>② 監視ダッシュボード</h4>
<div
class="POLL-TRIGGER"
hx-get="/htmx/demo/_poll_dashboard.php"
hx-trigger="load, every 5s"
hx-target="#DEMO_POLL_2_RESULT"
hx-swap="innerHTML"
aria-hidden="true"
></div>
<div id="DEMO_POLL_2_RESULT" class="CARD">
<p class="HTMX-NOTE">ここにダッシュボードが表示されます(自動更新)。</p>
</div>
</div>
PHP
<?php
// 型を厳密に扱う
declare(strict_types=1);
// セッションを開始する(指標とログを保持する)
session_start();
// HTMLとして返す
header('Content-Type: text/html; charset=UTF-8');
// HTMLエスケープ関数を用意する
function h(string $s): string {
// 特殊文字をエスケープする
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
// ダッシュボード状態が無ければ初期化する
if (!isset($_SESSION['demo_poll_dashboard']) || !is_array($_SESSION['demo_poll_dashboard'])) {
// 初期状態を作る
$_SESSION['demo_poll_dashboard'] = [
'queue' => 12,
'errors' => 0,
'latency' => 180,
'logs' => [],
'updated' => date('H:i:s'),
];
}
// 状態を取り出す
$st = (array)$_SESSION['demo_poll_dashboard'];
// 指標を少し変化させる
$st['queue'] = max(0, (int)($st['queue'] ?? 0) + random_int(-3, 6));
// エラー数を少し変化させる
$st['errors'] = max(0, (int)($st['errors'] ?? 0) + random_int(-1, 2));
// 平均応答(ms)を少し変化させる
$st['latency'] = max(40, (int)($st['latency'] ?? 0) + random_int(-30, 45));
// 更新時刻を入れる
$st['updated'] = date('H:i:s');
// ログ配列を取り出す
$logs = (array)($st['logs'] ?? []);
// ログの種類を用意する
$types = ['INFO', 'WARN', 'ERROR'];
// ログの本文候補を用意する
$msgs = [
'キューを処理しました。',
'バッチ処理が開始されました。',
'外部APIが遅延しています。',
'リトライを実行しました。',
'処理が完了しました。',
'一部のレコードで検証エラーが発生しました。',
];
// ログを1件追加する
$logs[] = [
'time' => date('H:i:s'),
'type' => $types[random_int(0, count($types) - 1)],
'msg' => $msgs[random_int(0, count($msgs) - 1)],
];
// ログを最大8件に制限する
$logs = array_slice($logs, -8);
// 状態へ戻す
$st['logs'] = $logs;
// セッションへ保存する
$_SESSION['demo_poll_dashboard'] = $st;
// カード表示を描画する
echo '<div style="display:grid; gap:.9rem;">';
// 見出し行を描画する
echo '<div><strong>監視ダッシュボード</strong> <span class="HTMX-NOTE">(更新:' . h((string)$st['updated']) . ')</span></div>';
// 指標カードのグリッドを描画する
echo '<div class="GRID">';
// キュー数カードを描画する
echo '<div class="CARD" style="background:rgba(0,0,0,.02);">';
echo '<div class="HTMX-NOTE">待ちキュー</div>';
echo '<div style="font-size:1.35rem;"><strong>' . h((string)$st['queue']) . '</strong> 件</div>';
echo '</div>';
// エラー数カードを描画する
echo '<div class="CARD" style="background:rgba(0,0,0,.02);">';
echo '<div class="HTMX-NOTE">直近エラー</div>';
echo '<div style="font-size:1.35rem;"><strong>' . h((string)$st['errors']) . '</strong> 件</div>';
echo '</div>';
// 応答時間カードを描画する
echo '<div class="CARD" style="background:rgba(0,0,0,.02);">';
echo '<div class="HTMX-NOTE">平均応答</div>';
echo '<div style="font-size:1.35rem;"><strong>' . h((string)$st['latency']) . '</strong> ms</div>';
echo '</div>';
// グリッドを閉じる
echo '</div>';
// ログ表を描画する
echo '<table>';
echo '<thead><tr><th>時刻</th><th>種別</th><th>内容</th></tr></thead>';
echo '<tbody>';
// ログを上から描画する(新しい順に見せたいので逆順)
foreach (array_reverse($logs) as $r) {
// 行を描画する
echo '<tr>';
echo '<td>' . h((string)($r['time'] ?? '')) . '</td>';
echo '<td><span class="TAG">' . h((string)($r['type'] ?? '')) . '</span></td>';
echo '<td>' . h((string)($r['msg'] ?? '')) . '</td>';
echo '</tr>';
}
// tbodyを閉じる
echo '</tbody>';
// tableを閉じる
echo '</table>';
// 閉じる
echo '</div>';
デモ
② 監視ダッシュボード
ここにダッシュボードが表示されます(自動更新)。
解説
-
ダッシュボード全体を
hx-getで取得し、結果エリアにinnerHTMLで差し替えています。
カード+ログのように “まとまり” があるUIは、まとめて更新した方が整合性が保てます。 -
更新間隔を少し長め(例:5秒)にすることで、サーバ負荷や通信量を抑えやすくなります。
監視の目的に合わせて「どれくらいの新しさが必要か」を決めるのがコツです。 -
「指標の数値が変わった」「ログが増えた」など、変化が目に見える構成にすると“動いてる感”が出ます。
定期更新は、数値や履歴が変化するUIと相性がとても良いです。
③ 手軽にリアルタイム風(新着イベントを追加)
新着イベントを定期的に取得し、一覧の先頭に追加して“リアルタイム風”に見せるデモです。
通知・更新履歴・監査ログなどを、自然に流れてくるように表現できます。
HTML
<div class="DEMO">
<h4>③ 手軽にリアルタイム風(新着イベントを追加)</h4>
<div
class="POLL-TRIGGER"
hx-get="/htmx/demo/_poll_realtime_feed.php"
hx-trigger="load, every 2s"
hx-target="#DEMO_POLL_3_FEED"
hx-swap="afterbegin"
aria-hidden="true"
></div>
<div class="CARD">
<ul id="DEMO_POLL_3_FEED" class="FEED">
<li class="HTMX-NOTE">ここに新着イベントが追加されます(自動更新)。</li>
</ul>
</div>
</div>
PHP
<?php
// 型を厳密に扱う
declare(strict_types=1);
// セッションを開始する(連番を保持する)
session_start();
// HTMLとして返す
header('Content-Type: text/html; charset=UTF-8');
// HTMLエスケープ関数を用意する
function h(string $s): string {
// 特殊文字をエスケープする
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
// フィード状態が無ければ初期化する
if (!isset($_SESSION['demo_poll_feed']) || !is_array($_SESSION['demo_poll_feed'])) {
// 初期状態を作る
$_SESSION['demo_poll_feed'] = [
'seq' => 9000,
];
}
// 状態を取り出す
$st = (array)$_SESSION['demo_poll_feed'];
// 連番を進める
$st['seq'] = (int)($st['seq'] ?? 0) + 1;
// セッションへ保存する
$_SESSION['demo_poll_feed'] = $st;
// イベント種別を用意する
$types = ['作成', '更新', '承認', '差し戻し', '削除'];
// イベント本文候補を用意する
$msgs = [
'申請を作成しました。',
'担当者を変更しました。',
'ステータスを更新しました。',
'コメントが追加されました。',
'明細が修正されました。',
];
// 種別を選ぶ
$type = $types[random_int(0, count($types) - 1)];
// 本文を選ぶ
$msg = $msgs[random_int(0, count($msgs) - 1)];
// 表示を返す(liだけ返してafterbeginで追加される)
echo '<li>';
echo '<div><strong>#' . h((string)$st['seq']) . '</strong> <span class="TAG">' . h($type) . '</span></div>';
echo '<div class="HTMX-NOTE">' . h($msg) . '(' . h(date('H:i:s')) . ')</div>';
echo '</li>';
デモ
③ 手軽にリアルタイム風(新着イベントを追加)
- ここに新着イベントが追加されます(自動更新)。
解説
-
hx-swap="afterbegin"を使うことで、新しい要素を先頭に“積み上げる”表示にできます。
差し替えではなく追加になるため、「新着が増えている」印象が直感的に伝わります。 -
返却するHTMLは
<li>など最小単位にすると、描画が軽くなります。
リアルタイム風表示は回数が増えやすいので、“小さく返す”設計が安心です。 -
表示が増え続けるケースでは、スクロール領域にする・件数を絞るなどの工夫が有効です。
運用時は「増えすぎ対策」まで考えておくと、実務でもそのまま使いやすくなります。
次に読むオススメレシピ
参考リンク
このページの著者
経験:Webアプリ/業務システム
得意:PHP・JavaScript・MySQL・CSS
個人実績:フォーム生成基盤/クイズ学習プラットフォーム 等
詳しいプロフィールはこちら! もちもちみかんのプロフィール