無限スクロールは、ユーザーがページを下にスクロールするたびに新しいコンテンツを動的にロードする手法です。
これにより、ページ全体を一度に読み込む必要がなくなり、パフォーマンスが向上します。
従来、無限スクロールの実装にはJavaScriptによる複雑な処理が必要でした。
しかし、htmxを使えばシンプルなHTMLの属性だけで実現できます。
この記事では、htmxを使った無限スクロールの実装方法を詳しく解説します。
本記事の内容
- 無限スクロールのHTML構造
- コンテンツの動的ロード
- ローディングインジケータの表示
- コンテンツの終わりの検出
- サンプルコード
- まとめ
それでは、上記に沿って解説していきます。
無限スクロールのHTML構造
htmxについてよくわからない場合は、次の記事からご覧ください。
まず、無限スクロールのHTML構造を定義します。
コンテンツを表示するコンテナと、新しいコンテンツをロードするトリガー要素を以下のように配置します。
<div id="content-container">
<!-- 初期コンテンツ -->
<div class="item">Initial Item #1</div>
<div class="item">Initial Item #2</div>
<div class="item">Initial Item #3</div>
<div id="load-trigger" hx-get="load_content.php?page=1" hx-trigger="revealed" hx-target="#content-container" hx-swap="beforeend">
<span class="loading-indicator">Loading...</span>
</div>
</div>
#load-trigger要素には、hx-get、hx-trigger、hx-target、hx-swapの属性を設定します。
hx-triggerを”revealed”にすることで、要素が画面内に表示された時点でトリガーが発火します。
hx-getで指定したPHPファイルからコンテンツを取得し、hx-targetで指定したコンテナの最後に追加します。
コンテンツの動的ロード
次に、PHPを使ってコンテンツを動的に生成します。
load_content.phpというPHPファイルを作成し、以下のようなコードを記述します。
<?php
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$items_per_page = 10;
$total_items = 100;
$start_index = ($page - 1) * $items_per_page;
$end_index = min($start_index + $items_per_page, $total_items);
for ($i = $start_index; $i < $end_index; $i++) {
echo "<div class='item'>Item #{$i}</div>";
}
// コンテンツの終わりを検出
if ($end_index < $total_items) {
echo "<div id='load-trigger' hx-get='load_content.php?page=" . ($page + 1) . "' hx-trigger='revealed' hx-target='#content-container' hx-swap='beforeend'>
<span class='loading-indicator'>Loading...</span>
</div>";
} else {
echo "<div id='end-of-content' class='hidden' hx-swap='outerHTML'>コンテンツの終わりに達しました。</div>";
}
?>
このPHPファイルでは、ページ番号に基づいてコンテンツを生成しています。
$total_items変数で全アイテム数を定義し、1ページあたりのアイテム数を$items_per_page変数で指定します。
生成したコンテンツの最後に、次のコンテンツをロードするための#load-trigger要素を追加します。
ただし、$end_indexが$total_itemsに達した場合は、次のコンテンツがないことを意味することになります。
そのため、代わりに#end-of-content要素を追加します。
この要素は、class=’hidden’を持ち、初期状態では非表示になります。
htmxは、#load-trigger要素が画面内に表示されたときに、load_content.phpを再度呼び出して新しいコンテンツを取得します。
コンテンツの終わりが検出された場合、#end-of-content要素が表示されます。
ローディングインジケータの表示
無限スクロールでは、新しいコンテンツのロード中にローディングインジケータを表示すると良いでしょう。
以下のようなCSSを使って、ローディングインジケータを実装します。
.loading-indicator {
display: none;
}
.loading-indicator.htmx-indicator {
display: inline-block;
}
htmx-indicatorクラスは、htmxがAjaxリクエストを開始したときに要素に追加されます。
これを利用して、ローディングインジケータの表示/非表示を切り替えます。
コンテンツの終わりの検出
無限スクロールを実装する際、コンテンツの終わりを検出して適切に処理することが重要です。
PHPコードでは、$end_indexが$total_itemsに達した場合、#end-of-content要素を追加します。
// コンテンツの終わりを検出
if ($end_index < $total_items) {
echo "<div id='load-trigger' hx-get='load_content.php?page=" . ($page + 1) . "' hx-trigger='revealed' hx-target='#content-container' hx-swap='beforeend'>
<span class='loading-indicator'>Loading...</span>
</div>";
} else {
echo "<div id='end-of-content'>コンテンツの終わりに達しました。</div>";
}
$end_indexが$total_itemsより小さい場合は、次のコンテンツをロードするための#load-trigger要素を追加します。
これにより、ユーザーがスクロールを続けると、次のコンテンツが動的にロードされます。
一方、$end_indexが$total_itemsに達した場合は、#end-of-content要素を追加します。
この要素には「コンテンツの終わりに達しました。」というメッセージが含まれており、コンテンツの終わりに達したことをユーザーに通知します。
#end-of-content要素には、class=’hidden’やhx-swap=’outerHTML’などの属性は付与されていません。
これにより、コンテンツの終わりが検出された時点で、このメッセージが表示されるようになります。
htmxは、#load-trigger要素が画面内に表示されたときに、load_content.phpを再度呼び出して新しいコンテンツを取得します。
コンテンツの終わりが検出された場合、#load-trigger要素は追加されず、代わりに#end-of-content要素が表示されます。
これにより、無限スクロールが停止し、ユーザーにコンテンツの終わりを通知します。
サンプルコード
以上の内容を組み合わせた完全なサンプルコードは以下の通りです。
PHPコード (load_content.php)
<?php
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$items_per_page = 10;
$total_items = 100;
$start_index = ($page - 1) * $items_per_page;
$end_index = min($start_index + $items_per_page, $total_items);
for ($i = $start_index; $i < $end_index; $i++) {
echo "<div class='item'>Item #{$i}</div>";
}
// コンテンツの終わりを検出
if ($end_index < $total_items) {
echo "<div id='load-trigger' hx-get='load_content.php?page=" . ($page + 1) . "' hx-trigger='revealed' hx-target='#content-container' hx-swap='beforeend'>
<span class='loading-indicator'>Loading...</span>
</div>";
} else {
echo "<div id='end-of-content'>コンテンツの終わりに達しました。</div>";
}
?>
HTMLコード (index.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>無限スクロールによるコンテンツの動的ロード</title>
<script src="https://unpkg.com/htmx.org@1.7.0"></script>
<style>
.loading-indicator {
display: none;
}
.loading-indicator.htmx-indicator {
display: inline-block;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div id="content-container">
<!-- 初期コンテンツ -->
<div class="item">Initial Item #1</div>
<div class="item">Initial Item #2</div>
<div class="item">Initial Item #3</div>
<div id="load-trigger" hx-get="load_content.php?page=1" hx-trigger="revealed" hx-target="#content-container" hx-swap="beforeend">
<span class="loading-indicator">Loading...</span>
</div>
</div>
</body>
</html>
このコードを実行すると、シンプルで高速な無限スクロールが実現されます。
コンテンツの終わりが検出された場合、「コンテンツの終わりに達しました。」というメッセージが表示されます。
まとめ
この記事では、htmxとPHPを組み合わせて無限スクロールを実装する方法を解説しました。
htmxのhx-trigger=”revealed”を使うことで、要素が画面内に表示されたタイミングでコンテンツの動的ロードをトリガーできます。
また、ローディングインジケータやエラーハンドリングを実装することで、ユーザーにとって使いやすいUIが作成できます。
さらに、コンテンツの終わりを検出し、適切に処理することで、無限スクロールが無限に続くことを防ぎます。
htmxとサーバーサイドの処理を連携させることで、高速で洗練された無限スクロールを実装できます。
この記事で紹介したテクニックを活用し、さらに発展させることで、ユーザーエンゲージメントを高める魅力的な無限スクロールを作成しましょう。


