もっと見る (htmx 段階展開)

更新

ページネーションの代わりに「もっと見る」で段階的に展開する UX。htmx + History API で SPA-lite なナビゲーションを実現。

md2docs では従来のページネーションをやめて、「もっと見る」をクリックすると現在のリストに記事を追加していく UI を採用しています。一覧画面でページが切り替わる感覚がなく、左ナビの状態も保たれるので、ドキュメントを読みながら気になる項目を芋づる式に開いていけます。

実装は htmx が担当しており、JavaScript は最小限です。

4 箇所のもっと見る

場所 振る舞い URL に push?
左ナビ カテゴリ内記事 5 件ずつ展開 しない(純粋な UI 操作)
カテゴリ一覧 (/guide 等) 10 件ずつ累積 する(/guide/page/2 等)
タグ一覧 (/tag/foo) 10 件ずつ累積 する
検索結果 (/search?q=...) 10 件ずつ累積 する(?page=2 を追記)

累積ページネーション

メインカラムの「もっと見る」は /page/N の意味を「N ページ目までの累積」に再定義 しています。

URL 表示内容
/guide 1〜10 件目 + もっと見る (→ /guide/page/2)
/guide/page/2 1〜20 件目 + もっと見る (→ /guide/page/3)
/guide/page/3 1〜30 件目 + もっと見る

リロードしても URL とビジュアルが完全一致するため、Ajax 感がなく自然です。ブラウザの戻るボタンで /guide に戻ると最初の 10 件状態に戻ります。

メモ

hx-push-url="true" を指定しているため、もっと見るボタンを押すたびに URL が /guide/page/N に更新されます。ブックマークしたり共有したりすると、その時点で開いている件数の状態のまま閲覧できます。

SPA-lite ナビゲーション

記事リンクをクリックした時もページ全体は再読み込みせず、メインカラム(#main-content)と右サブカラムの「このページの内容」(#toc-content)だけが htmx の hx-boost + OOB swap で入れ替わります。

つまり:

  • 左ナビの「もっと見る」展開状態は保たれる — 6 件展開してから別の記事を開いてもナビは縮まない
  • お気に入りリストも保たれる — JS で localStorage を再読み込みする必要がない
  • 戻る/進むボタンが htmx の history で動く — フルリロードなし
  • メインコンテンツ遷移時は自動でスクロール TOP に滑らかに戻る

外部リンク(GitHub アイコン等)には hx-boost="false" を付けて通常遷移にしています。

設定

config/site.yaml で初期件数と増分を指定できます。

site:
  nav_initial_count:    5    # 左ナビ: 初期 5 件
  nav_load_more_count:  5    # 左ナビ: もっと見るで +5 件
  list_initial_count:  10    # 一覧: 初期 10 件
  list_load_more_count: 10   # 一覧: もっと見るで +10 件

ヒント

記事数が少ないサイトでは初期件数を大きめに設定し、もっと見るが出ないようにしておくのも手です。逆に左ナビをコンパクトにしたい場合は nav_initial_count: 3 くらいに絞ると見通しが良くなります。

仕組みのまとめ

サーバ側:

  • Application::cumulativeListCount($page)初期 + (N-1) × 増分 を返す
  • Application::renderCategory/renderTag/renderSearchRequest::wantsPartial() を見て、フルページか部分テンプレ(partials/list_with_more.html.twig)かを切替
  • 左ナビの専用エンドポイント /_partial/nav/{path} がナビ片だけを返す

クライアント側:

  • body に hx-boost="true" hx-target="#main-content" hx-select="#main-content" hx-select-oob="#toc-content" を付与
  • 各「もっと見る」ボタンで body の hx-select 継承を上書きするため hx-select="#article-list-loadmore" などをスコープ指定
  • htmx.config.scrollIntoViewOnBoost = false にして htmx の自動スクロールを抑止し、htmx-bridge.jsrequestAnimationFrame 2 フレーム待ってから滑らかに上部スクロール

メモ

JS が無効な環境でも素のページネーションリンクとして動作します(<a href="/guide/page/2">)。