htmx 逆引きレシピ
htmxでボタンで一部だけ差し替えるには?
htmxでは、ボタン1つに hx-get / hx-target / hx-swap を付けるだけで、
ページ全体ではなく必要な範囲だけを再取得して差し替えられます。
管理画面の「詳細パネル」「承認ステータス」「集計カード」など、部分更新が多い業務UIで特に効きます。
使用するhtmx属性
hx-get:GETでHTMLを取りに行って差し替えるhx-post:POSTで更新して、結果のHTMLを差し替えるhx-target:差し替える先の要素(CSSセレクタ)を指定hx-swap:差し替え方を指定(例:innerHTML/outerHTML)hx-indicator:通信中表示(ローディング)を出す要素を指定hx-disabled-elt:通信中だけボタン等をdisabledにして連打を防止
利用シーン
- 「詳細パネルだけ再読み込み」:DB更新後の最新状態を、そのパネルだけ反映したい
- 「承認ステータスだけ更新」:ステータスバッジだけ差し替えたい
- 「集計カードを更新」:ダッシュボードの集計結果だけ再計算して差し替えたい
① 詳細パネルだけ再読み込み(ボタン → パネル丸ごと差し替え)
ボタン押下で、詳細パネルだけを再取得し、outerHTML でパネル要素ごと差し替えます。
(返すHTMLに同じ id を含めるのがコツです)
HTML
<div class="DEMO">
<!-- ①:差し替え対象のパネル(初期表示は通常レンダリングでもOK) -->
<section id="DEMO_DETAIL_PANEL" class="PANEL" aria-label="詳細パネル">
<h4>ユーザー詳細</h4>
<p><strong>ID:</strong>42</p>
<p><strong>最終更新:</strong>2025-12-30 18:00</p>
<p><strong>メモ:</strong>…</p>
</section>
<!--
このボタンを押すと、詳細パネルだけを再読み込みします。
hx-get : 取りに行くURL(HTML断片が返る)
hx-target : 差し替える先(#DETAIL_PANEL)
hx-swap : outerHTML=箱ごと差し替える
hx-indicator : 通信中だけ表示する要素(ローディング)
hx-disabled-elt : 通信中だけボタンをdisabled(連打防止)
-->
<button
type="button"
class="BTN"
hx-get="/htmx/demo/_detail_panel.php?id=123"
hx-target="#DEMO_DETAIL_PANEL"
hx-swap="outerHTML"
hx-indicator="#DETAIL_PANEL_LOADING"
hx-disabled-elt="this"
>
詳細パネルを再読み込み
</button>
<!-- 通信中だけ出したい表示(CSSで .htmx-request を使う) -->
<span id="DETAIL_PANEL_LOADING" class="LOADING" aria-live="polite">
更新中…
</span>
</div>
PHP
<?php
// ① 詳細パネル断片(outerHTMLで丸ごと差し替え)
// 型のミスを減らして堅くする(任意だけどおすすめ)
declare(strict_types=1);
// 返すのはHTML断片
header('Content-Type: text/html; charset=UTF-8');
// GETパラメータ id を整数に(無ければ0)
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
// 本来はDBから最新を取得する想定(ここでは例として固定)
$data = [
// 表示用ID
'id' => $id,
// 例:いまの時刻を最終更新として表示
'updated_at' => date('Y-m-d H:i:s'),
// 例文
'memo' => 'サーバー側で再取得した最新データです(例)',
];
?>
<!-- outerHTMLで差し替えるので、同じ id="DETAIL_PANEL" を返すのがコツ -->
<section id="DEMO_DETAIL_PANEL" class="PANEL" aria-label="詳細パネル">
<h4>ユーザー詳細</h4>
<p>
<strong>ID:</strong>
<!-- XSS対策:表示前に必ずエスケープ -->
<?= htmlspecialchars((string)$data['id'], ENT_QUOTES, 'UTF-8') ?>
</p>
<p>
<strong>最終更新:</strong>
<!-- XSS対策 -->
<?= htmlspecialchars($data['updated_at'], ENT_QUOTES, 'UTF-8') ?>
</p>
<p>
<strong>メモ:</strong>
<!-- XSS対策 -->
<?= htmlspecialchars($data['memo'], ENT_QUOTES, 'UTF-8') ?>
</p>
</section>
デモ
ユーザー詳細
ID:42
最終更新:2025-12-30 18:00
メモ:…
解説
- hx-swap="outerHTML" は「箱ごと差し替え」なので、パネルの構造(見出しや属性)も含めて一括更新できます。
- 返す断片に id="DETAIL_PANEL" を含めているのは、次回の差し替え先がブレないようにするためです。
- hx-indicator と hx-disabled-elt をセットにすると、通信中に「待ってね」が伝わり、連打事故も防げます。
コツ:詳細パネルのような「まとまりのあるUI」は outerHTML 更新にすると、設計がシンプルで保守しやすくなります。
② 承認ステータスだけ更新(ボタン → バッジだけ差し替え)
差し替え対象が小さいほど、htmxの気持ちよさが出ます。
ここでは「承認」ボタンで、ステータスバッジだけ差し替えます。
HTML
<div class="DEMO">
<p>
<!-- 表示ラベル -->
ステータス:
<!-- 差し替え対象:このバッジだけ更新したい -->
<span id="APPROVAL_BADGE" class="BADGE is-pending">
未承認
</span>
<!-- 通信中だけ出したい表示 -->
<span id="APPROVAL_LOADING" class="LOADING" aria-live="polite">
更新中…
</span>
</p>
<!--
②:更新ボタン(承認にする)
hx-post : 更新先(HTML断片を返す)
hx-target : 差し替える先(#APPROVAL_BADGE)
hx-swap : outerHTML=バッジ要素ごと差し替える(classも更新しやすい)
hx-indicator : 通信中表示
hx-disabled-elt : 連打防止
-->
<button
type="button"
class="BTN"
hx-post="/htmx/demo/_approval_badge.php?id=42"
hx-target="#APPROVAL_BADGE"
hx-swap="outerHTML"
hx-indicator="#APPROVAL_LOADING"
hx-disabled-elt="this"
>
承認にする
</button>
</div>
PHP
<?php
// ② 承認バッジ断片(outerHTMLで差し替え)
// 任意:型を堅くする
declare(strict_types=1);
// HTML断片を返す
header('Content-Type: text/html; charset=UTF-8');
// 更新対象ID(例)
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
// 本来はここでDBを更新する想定(例)
// updateApproval($id, true);
?>
<!-- hx-target="#APPROVAL_BADGE" に対して outerHTML で差し替える -->
<span id="APPROVAL_BADGE" class="BADGE is-approved">
承認済み
</span>
デモ
ステータス: 未承認 更新中…
解説
- 差し替え対象が小さいので、hx-target="#APPROVAL_BADGE" でバッジにピンポイントで当てています。
- outerHTML だと、テキストだけでなく class(色) も含めて差し替えできるため、状態表現が楽になります。
- 更新処理はサーバー側(DB更新など)に寄せ、返すのは「更新後のバッジHTML」にするのが、htmxらしい設計です。
コツ:ステータス・チップ・ラベルなど「小さい部品」は、targetを狭くして“気持ちいい部分更新”を作ると効果的です。
③ 集計カードを更新(ボタン → カード群だけ差し替え)
ダッシュボードの「カード群」など、複数要素をまとめて更新したい場合は、
カードの入れ物(wrap)に対して innerHTML 差し替えにするとシンプルです。
HTML
<div class="DEMO">
<!-- ③:差し替え対象(カード群の入れ物) -->
<div id="CARD_WRAP" class="CARD_WRAP">
<div class="CARD"><strong>未処理</strong><span>12</span></div>
<div class="CARD"><strong>承認待ち</strong><span>5</span></div>
<div class="CARD"><strong>本日完了</strong><span>3</span></div>
</div>
<!--
③:集計カード群を更新するボタン
hx-get : 集計カード群(HTML断片)を返すURL
hx-target : 差し替える先(#CARD_WRAP)
hx-swap : innerHTML=入れ物の中身だけ差し替える(箱は残す)
hx-indicator : 通信中表示
hx-disabled-elt : 連打防止
-->
<button
type="button"
class="BTN"
hx-get="/htmx/demo/_summary_cards.php"
hx-target="#CARD_WRAP"
hx-swap="innerHTML"
hx-indicator="#SUMMARY_LOADING"
hx-disabled-elt="this"
>
集計カードを更新
</button>
<!-- 通信中だけ出したい表示 -->
<span id="SUMMARY_LOADING" class="LOADING" aria-live="polite">
更新中…
</span>
</div>
PHP
<?php
// ③ 集計カード群断片(innerHTMLで差し替え)
// - CARD_WRAP の中に入る「カードたち」だけを返す
// 任意:堅くする
declare(strict_types=1);
// HTML断片を返す
header('Content-Type: text/html; charset=UTF-8');
// 本来はDBや集計処理で値を作る想定(ここでは例)
$cards = [
// カード1
['label' => '未処理', 'value' => random_int(0, 20)],
// カード2
['label' => '承認待ち', 'value' => random_int(0, 20)],
// カード3
['label' => '本日完了', 'value' => random_int(0, 20)],
];
// カード配列を順番にHTMLとして出力する
foreach ($cards as $c) {
// 表示ラベル(XSS対策)
$label = htmlspecialchars($c['label'], ENT_QUOTES, 'UTF-8');
// 数値も文字列としてエスケープ(XSS対策)
$value = htmlspecialchars((string)$c['value'], ENT_QUOTES, 'UTF-8');
// カード開始
echo '<div class="CARD">';
// 左側:ラベル
echo '<strong>' . $label . '</strong>';
// 右側:値
echo '<span>' . $value . '</span>';
// カード終了
echo '</div>';
}
デモ
解説
- hx-swap="innerHTML" は「入れ物は残して中身だけ差し替え」なので、カード群の更新に向きます。
- サーバーは CARD_WRAP の中に入るカードHTMLだけを返す設計にすると、責務が明確になって保守しやすいです。
- 集計処理は重くなりがちなので、必要なときだけ更新する(ボタン更新)は業務UIで扱いやすいパターンです。
コツ:複数カードをまとめて更新するなら「wrapは固定・中身だけ返す」にすると、差し替えが単純になります。
参考リンク
このページの著者
経験:Webアプリ/業務システム
得意:PHP・JavaScript・MySQL・CSS
個人実績:フォーム生成基盤/クイズ学習プラットフォーム 等
詳しいプロフィールはこちら! もちもちみかんのプロフィール