htmx 逆引きレシピ
スクロール到達で遅延読み込みするには?

公開日:
最終更新日:

遅延読み込みは、最初の画面表示を軽くして「必要になった部分だけ後で読む」ための定番パターンです。

htmxなら、hx-trigger="revealed" でスクロール到達時に読み込みを発火し、該当ブロックだけ差し替えできます。

このページでは、重いカード本文・タブの初回読込・添付プレビューを3デモでまとめます。

使用するhtmx属性

タグ:hx-get / hx-trigger / hx-target / hx-swap

  • hx-get:遅延で読みたいHTML断片(本文/タブ中身/プレビュー)を取得します。
  • hx-trigger:発火条件を指定します。今回は revealed once を基本に、タブでは click once も使い分けます。
  • hx-target:返却HTMLを差し込む先を指定します(例:this / 特定パネル)。
  • hx-swap:差し替え方を指定します。遅延読込では outerHTML でプレースホルダごと置換する形が扱いやすいです。

利用シーン

  • 「重い集計カード」:ダッシュボードの集計やサマリは先に概要だけ出し、詳細は到達したら読みたい
  • 「タブの中身は開いたら読む」:見ないタブまで最初に取らず、クリックしたタブだけ中身を読みたい
  • 「画像/添付の一覧だけ後で読む」:一覧は軽く出して、プレビューや詳細は見える分だけ後から読みたい

① 最小構成(revealedで“重いカード本文”だけ遅延)

カードの枠と見出しは先に表示し、本文だけをスクロール到達で読み込みます。
最初はスケルトンを置き、読み込み完了で本文へ差し替える最小構成です。

HTML

<div class="DEMO">

	<h4>① 最小構成(revealedで“重いカード本文”だけ遅延)</h4>

	<article class="CARD">
		<h4>月次集計カード</h4>
		<p class="INF_META">概要だけ先に表示。本文は到達時に読込。</p>
		<div
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_1.php?card=monthly"
			hx-trigger="revealed once"
			hx-target="this"
			hx-swap="outerHTML"
		>
			読み込み準備中...
		</div>
	</article>

</div>

PHP

<?php
// 文字コード付きでHTMLを返す
header('Content-Type: text/html; charset=UTF-8');

// セッションを開始する(統一のため)
session_start();

// 共通データを読み込む
require __DIR__ . '/_lazy_load_data.php';

// カードIDを受け取る
$card = (string)($_GET['card'] ?? 'monthly');

// 前後空白を除去する
$card = trim($card);

// カード本文を取得する
$body = lazy_load_get_card_body($card);

// 取得失敗時は表示文言を補正する
if ($body === null) {
	// 代替本文を作る
	$body = [
		'title' => 'データなし',
		'lines' => ['対象カードが見つかりませんでした。'],
	];
}

// カード本文を開始する
echo '<div class="CARD MT32">';

// 見出しを出す
echo '<strong>' . lazy_load_h((string)$body['title']) . '</strong>';

// 補足を出す
echo '<p class="INF_META">本文のみ遅延読み込み完了</p>';

// 本文行を順に出す
foreach ((array)$body['lines'] as $line) {
	// 1行を表示する
	echo '<p>' . lazy_load_h((string)$line) . '</p>';
}

// カード本文を閉じる
echo '</div>';

// デモ用の遅延
usleep(3000000);

デモ

① 最小構成(revealedで“重いカード本文”だけ遅延)

月次集計カード

概要だけ先に表示。本文は到達時に読込。

読み込み準備中...

解説

  • hx-trigger="revealed once" で、要素が見えた1回だけ本文取得を実行します。
  • 差し替え対象を hx-target="this" にし、hx-swap="outerHTML" でスケルトン枠ごと本文へ置換します。
  • 初回表示では重い本文生成を回避できるため、一覧画面の体感表示を軽くできます。

② タブ(開いた時に初回だけ読み込み)

タブ本体は先に出し、中身は各タブの表示領域に入ったタイミングで初回だけ読み込みます。
「押した瞬間に確実に読む」なら click once、「表示されたら読む」なら revealed once を使います。

HTML

<div class="DEMO">

	<h4>② タブ(開いた時に初回だけ読み込み)</h4>

	<details open>
		<summary>売上サマリ</summary>
		<div
			id="DEMO_LAZY_2_SALES"
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_2.php?tab=sales"
			hx-trigger="revealed once"
			hx-target="#DEMO_LAZY_2_SALES"
			hx-swap="outerHTML"
		>
			読み込み準備中...
		</div>
	</details>

	<details>
		<summary>部署別</summary>
		<div
			id="DEMO_LAZY_2_DEPT"
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_2.php?tab=dept"
			hx-trigger="revealed once"
			hx-target="#DEMO_LAZY_2_DEPT"
			hx-swap="outerHTML"
		>
			読み込み準備中...
		</div>
	</details>

	<details>
		<summary>異常値</summary>
		<div
			id="DEMO_LAZY_2_ALERT"
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_2.php?tab=alert"
			hx-trigger="revealed once"
			hx-target="#DEMO_LAZY_2_ALERT"
			hx-swap="outerHTML"
		>
			読み込み準備中...
		</div>
	</details>

</div>

PHP

<?php
// 文字コード付きでHTMLを返す
header('Content-Type: text/html; charset=UTF-8');

// セッションを開始する(統一のため)
session_start();

// 共通データを読み込む
require __DIR__ . '/_lazy_load_data.php';

// タブIDを受け取る
$tab = (string)($_GET['tab'] ?? 'sales');

// 前後空白を除去する
$tab = trim($tab);

// タブ本文を取得する
$panel = lazy_load_get_tab_panel($tab);

// 取得失敗時は表示文言を補正する
if ($panel === null) {
	// 代替本文を作る
	$panel = [
		'title' => 'データなし',
		'items' => ['指定タブが見つかりませんでした。'],
	];
}

// パネルを開始する
echo '<section class="CARD">';

// 見出しを出す
echo '<strong>' . lazy_load_h((string)$panel['title']) . '</strong>';

// 箇条書きを開始する
echo '<ul class="HTMX-LIST">';

// 項目を順に出す
foreach ((array)$panel['items'] as $item) {
	// 項目を1つ出す
	echo '<li>' . lazy_load_h((string)$item) . '</li>';
}

// 箇条書きを閉じる
echo '</ul>';

// 注記を出す
echo '<p class="INF_META">このタブは初回表示時のみ取得しています。</p>';

// パネルを閉じる
echo '</section>';

// デモ用の遅延
usleep(3000000);

デモ

② タブ(開いた時に初回だけ読み込み)

売上サマリ
読み込み準備中...
部署別
読み込み準備中...
異常値
読み込み準備中...

解説

  • 各タブパネル内にプレースホルダを置き、パネルが表示された瞬間に初回読込します。
  • 開閉後に再取得したくないため、once を付けて同一パネルでの再発火を防ぎます。
  • クリック直後に必ず先読みしたいUIでは、同じURLに対して hx-trigger="click once" をタブボタン側へ付ける運用も有効です。

③ 画像/添付(一覧は先に出し、プレビューはスクロール到達で遅延)

一覧(ファイル名やサイズ)は先に描画し、重いプレビュー枠だけを遅延読み込みします。
添付が多い画面でも、まず一覧操作を優先して軽く始められます。

HTML

<div class="DEMO">

	<h4>③ 画像/添付(一覧は先に出し、プレビューはスクロール到達で遅延)</h4>

	<div class="CARD">
		<strong>見積書_2026_Q1.pdf</strong>
		<div class="INF_META">2.1MB / 経理部</div>
		<div
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_3.php?id=att-001"
			hx-trigger="revealed once"
			hx-target="this"
			hx-swap="outerHTML"
		>
			プレビュー読込準備中...
		</div>
	</div>

	<div class="CARD MT32">
		<strong>契約書_再締結.docx</strong>
		<div class="INF_META">860KB / 法務部</div>
		<div
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_3.php?id=att-002"
			hx-trigger="revealed once"
			hx-target="this"
			hx-swap="outerHTML"
		>
			プレビュー読込準備中...
		</div>
	</div>

	<div class="CARD MT32">
		<strong>写真_現地確認_01.jpg</strong>
		<div class="INF_META">3.4MB / 営業部</div>
		<div
			class="LOADING"
			hx-get="/htmx/demo/_lazy_load_3.php?id=att-003"
			hx-trigger="revealed once"
			hx-target="this"
			hx-swap="outerHTML"
		>
			プレビュー読込準備中...
		</div>
	</div>

</div>

PHP

<?php
// 文字コード付きでHTMLを返す
header('Content-Type: text/html; charset=UTF-8');

// セッションを開始する(統一のため)
session_start();

// 共通データを読み込む
require __DIR__ . '/_lazy_load_data.php';

// 添付IDを受け取る
$id = (string)($_GET['id'] ?? '');

// 前後空白を除去する
$id = trim($id);

// 添付情報を取得する
$att = lazy_load_get_attachment($id);

// 添付情報が無いときはエラーを返して終了する
if ($att === null) {
	// エラーブロックを出す
	echo '<div class="FORM-RESULT is-err">プレビューを表示できませんでした。</div>';
	// 処理を終了する
	exit;
}

// プレビュー枠を開始する
echo '<div class="CARD">';

// 見出しを出す
echo '<strong>Preview: ' . lazy_load_h((string)$att['name']) . '</strong>';

// メタ情報を出す
echo '<div class="INF_META">' . lazy_load_h((string)$att['size']) . ' / ' . lazy_load_h((string)$att['owner']) . '</div>';

// プレビュー本文を出す
echo '<p>' . lazy_load_h((string)$att['preview']) . '</p>';

// プレビュー枠を閉じる
echo '</div>';

// デモ用の遅延
usleep(3000000);

デモ

③ 画像/添付(一覧は先に出し、プレビューはスクロール到達で遅延)

見積書_2026_Q1.pdf
2.1MB / 経理部
プレビュー読込準備中...
契約書_再締結.docx
860KB / 法務部
プレビュー読込準備中...
写真_現地確認_01.jpg
3.4MB / 営業部
プレビュー読込準備中...

解説

  • 一覧情報(名前・サイズ)は初期表示し、重いプレビューだけ遅延することで初回表示を軽くします。
  • プレビュー枠ごとに revealed once を設定すると、見えた添付だけ順次読み込まれます。
  • サーバ側はIDを検証して対象添付のみ返す設計にし、無効ID時はエラーHTMLを返して安全に終了します。

次に読むオススメレシピ

このページの著者

もちもちみかん(システムエンジニア)

社内SEとしてグループ企業向けの業務アプリを要件定義〜運用まで一気通貫で担当しています。

経験:Webアプリ/業務システム

得意:PHP・JavaScript・MySQL・CSS

個人実績:フォーム生成基盤クイズ学習プラットフォーム

詳しいプロフィールはこちら!  もちもちみかんのプロフィール

もちもちみかん0系くん
TOPへ

もちもちみかん.comとは


このサイトでは、コーディングがめんどうくさい人向けのお助けツールとして、フォームやCSSをノーコードで生成できる、
 もちもちみかん.forms
 もちもちみかん.css1
 もちもちみかん.css2
と言ったジェネレーターを用意してます。

また、このサイトを通じて、「もちもちみかん」のかわいさを普及したいとかんがえてます!