[htmx] hx-triggerを使ってinfinitScrollを実装する方法[Java/Spring Boot]

はじめに

こんにちは、新卒2年目の岡崎です。今回はhx-triggerを使ってinfinitScrollを実装する方法を紹介します。

環境

  • gradle
------------------------------------------------------------
Gradle 8.5
------------------------------------------------------------

Build time:   2023-11-29 14:08:57 UTC
Revision:     28aca86a7180baa17117e0e5ba01d8ea9feca598

Kotlin:       1.9.20
Groovy:       3.0.17
Ant:          Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM:          21.0.2 (Amazon.com Inc. 21.0.2+13-LTS)
OS:           Mac OS X 12.3 aarch64
openjdk version "21.0.2" 2024-01-16 LTS
OpenJDK Runtime Environment Corretto-21.0.2.13.1 (build 21.0.2+13-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.2.13.1 (build 21.0.2+13-LTS, mixed mode, sharing)
  • Spring Boot
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.1)

今回は、htmxを使っているため、以下の依存関係をbuild.gradleに追加します。

implementation "org.webjars.npm:htmx.org:1.9.10"

実装

hx-triggerを使うことで、infinitScrollを簡単に実装できます。

hx-triggerの詳細は、公式ドキュメントをご覧ください。

htmx.org

Controller

まずは、Controllerにエンドポイントのを作成します。

  • /scrollは、最初にアクセスした際の画面表示用エンドポイントです。
  • /scroll/newは、スクロール時に呼び出されるエンドポイントです。
@Controller
@RequestMapping("scroll")
public class ScrollController {
    @GetMapping
    public String get(
            Model model
    ) {
        final List<String> list = List.of(
                "test1",
                "test2",
                "test3",
                "test4",
                "test5",
                "test6",
                "test7",
                "test8",
                "test9",
                "test10"
        );

        model.addAttribute("list", list);

        return "scroll/index";
    }

    @GetMapping("new")
    public String getNew(Model model) {
        final List<String> list = List.of(
                "newTest1",
                "newTest2",
                "newTest3",
                "newTest4",
                "newTest5",
                "newTest6",
                "newTest7",
                "newTest8",
                "newTest9",
                "newTest10"
        );

        model.addAttribute("list", list);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return "scroll/index :: scroll";
    }
}

html

  • scroll/layout.html

レイアウトファイルを作成しました。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="ja"
      th:fragment="layout(content)"
>
<head>
    <title>Demo</title>

    <script src="/webjars/htmx.org/1.9.10/dist/htmx.min.js"></script>
</head>
<body>
<header>
    header
</header>

<div th:replace="${content}">
    <p>Page content goes here</p>
</div>

<footer>
    footer
</footer>
</body>
</html>
  • scroll/index.html

画面にlistの内容を表示します。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="ja" th:replace="~{scroll/layout::layout(~{::content})}">
<body>
    <th:block th:fragment="content">
        <th:block th:insert="~{scroll/index::scroll}"></th:block>
    </th:block>

    <th:block th:fragment="scroll">
        <div class="container text-center mt-5">
            <th:block th:each="row : ${list}">
                <div th:text="${row}"></div>
            </th:block>
        </div>
        <div>
            <span hx-indicator="#indicator"
                  hx-target="closest div"
                  hx-trigger="revealed"
                  hx-swap="outerHTML"
                  th:hx-get="@{/scroll/new}">
                Loading......
            </span>
        </div>
    </th:block>
</body>
</html>

下記の部分がスクロール部分の実装です。

            <span
                  hx-target="closest div"
                  hx-trigger="revealed"
                  hx-indicator="#indicator"
                  hx-swap="outerHTML"
                  th:hx-get="@{/scroll/new}">
                Loading......
            </span>

今回は、スクロール時にリクエストを発火させたいので、hx-triggerrevealed を指定しました。これにより、指定された div タグがスクロールで表示されると、/scroll/new が呼び出されます。

また、hx-indicator="#indicator" を使用してローディング処理を実装しました。

最後に、スクロール時の挙動を確認できたら、完了です。

最後に

今回はhx-triggerを使ってinfinitScrollを実装する方法を紹介しました。みなさんもぜひ実装してみてください。