コンテンツにスキップ

データグリッドの無限スクロール

フィードや長いリストのための datagrid-pager の 姉妹レシピです: tbody の最終行がセンチネル — ビューポートに 入ると次バッチを GET し、outerHTML自分自身を新しい行 + 次の センチネルに置き換え、末尾マーカーが引退させるまで続きます。 カーソルは最終行の id(after=)で、ページ番号ではありません: 追記型のリストはオフセットページングの下でずれますが、id はずれ ません。新規 JavaScript ゼロ。 バージョニングポリシーの 対象です。

グリッドの中でスクロールしてください — ローディング行が コンテナの表示領域に入るたびに、自分を次の 5 行(全 15 行)と新しい センチネルに入れ替え、リストは「15 of 15」マーカーで閉じます。

このデモはコンテナスクロール変種です(intersect once root:…、 下のカーブアウトを参照): 15 行しかないデモを既定のウィンドウ ビューポート基準 revealed で動かすと、背の高い画面では新しい センチネルが常にすでに見えているため、スクロールする前に全件が 連鎖ロードされてしまいます。実際のページスクロールのフィード — 下のマークアップが示す形 — は revealed のままです。初期行も静的な docs サイトゆえのカーブアウトです: デモは最初のバッチを load トリガーで取得しますが、実サーバは 1 ページ目をインラインで描画 します。

IDProductPriceStock

まずグリッドのキャップを外します: hc-datagrid ルートに --hc-datagrid-max-height: none を指定し、ページをスクローラに します。既定のキャップ(70vh)はグリッドを自前のオーバーフロー コンテナに変えます — そして revealed はウィンドウビューポート 基準のトリガーなので、グリッドの内側であふれた番兵は二度と発火 しません: フィードは最初のバッチでデッドロックします。自前の スクロールバーを保ちたいグリッドは、代わりに下の intersect 変種を使ってください。

<div class="hc-datagrid" style="--hc-datagrid-max-height: none">
<div class="hc-datagrid__scroll">
<table class="hc-datagrid__table">
<thead class="hc-datagrid__head"></thead>
<tbody class="hc-datagrid__body">
<!-- Page 1 server-rendered — the grid is full without JS. -->
<tr class="hc-datagrid__row">
<th class="hc-datagrid__cell" scope="row">item-1</th>
<td class="hc-datagrid__cell">Compact Anvil</td>
<td class="hc-datagrid__cell">$107</td>
<td class="hc-datagrid__cell">12</td>
</tr>
<!-- …rows item-2 … item-5… -->
<!-- The sentinel: the last row, carrying the cursor. -->
<tr class="hc-datagrid__row"
data-hx-get="/items?after=item-5"
data-hx-trigger="revealed"
data-hx-swap="outerHTML">
<td class="hc-datagrid__cell" colspan="4" aria-live="polite">
<span class="hc-spinner" aria-hidden="true"></span> Loading…
</td>
</tr>
</tbody>
</table>
</div>
</div>

outerHTML スワップがトリック全体です: どの応答もセンチネルを更新 (バッチ + 次カーソル付きの新センチネル)するか、引退(バッチ + 末尾マーカー)させるかのどちらかなので、ローディング行は常に ちょうど 1 つです。エンドポイントは行だけを返します。フルページ しかレンダリングできないサーバーは data-hx-select="tbody > tr" を 足して行を切り出してください。

コンテナスクロールのグリッド

Section titled “コンテナスクロールのグリッド”

revealed が測るのはウィンドウのビューポートです。自前の スクロールバーを保つグリッド(既定の --hc-datagrid-max-height: 70vh や任意の overflow-y: scroll ラッパー)は、代わりにコンテナを認識 するトリガーが必要です。契約の他の部分は何も変わりません:

<tr class="hc-datagrid__row"
data-hx-get="/items?after=item-5"
data-hx-trigger="intersect once root:#feed-scroll threshold:0.5"
data-hx-swap="outerHTML">
</tr>

root: はスクロールコンテナのセレクタを取ります(通常は .hc-datagrid__scroll — id を付けてください)。once は所属する センチネルと一緒にオブザーバーを引退させ、threshold: は次のバッチを 読み込む前にどれだけ行が見えている必要があるかを決めます。 サーバーは更新したすべてのセンチネルに同じトリガーを反映する必要が あります — カーソル URL に root を通すか、テンプレートに埋め込んで ください(docs のデモはテンプレートに埋め込んでいます)。

これが避ける 2 つの失敗モード:

  • デッドロック — グリッドの内側であふれた revealed の センチネルはウィンドウビューポートに入らないので、フィードは最初の バッチで止まります。
  • 連鎖ロード — 背の高いウィンドウでは短いリストの新しい センチネルが最初から見えているため、すべてのバッチが即座に発火し、 「無限」リストが完成した状態で届きます。(上のライブデモが避けて いるのはこれです。数百行の実フィードが素の revealed で問題なく 動くのも同じ理由です。)

GET /items?after=<cursor>:

ケース応答(常に 200)
まだ行がある次の <tr> バッチ + 次カーソルを積んだ新しいセンチネル行
リストの末尾バッチ(空もあり)にセンチネルなし、末尾行で閉じる: <td colspan aria-live="polite">40 of 40</td>
古いカーソル最寄りの安定点からのバッチ — カーソルは再開可能で、4xx にはなりません。スクロールはエラーではありません

バッチの行は初期行を鏡写しにします(同じクラス、id の scope="row" ヘッダー)。スワップされた行はサーバーレンダリングの 1 ページ目と見分けがつきません。

プログレッシブエンハンスメント

Section titled “プログレッシブエンハンスメント”

1 ページ目はサーバーレンダリングなので、リストは届いた時点で有用 です。JavaScript なしではセンチネルはただの行です。全件に到達可能で ある必要があるなら、素朴な「もっと見る」リンク (<a href="/items?after=…">)を用意してください — 同じ エンドポイントが HX-Request 分岐でフラグメントとフルページの両方を 返します。

  • センチネル/末尾のセルは 1 つの aria-live="polite" スロットです: 「Loading…」と最後の「40 of 40」が、フォーカスを奪わずに読み上げ られます。
  • スピナーは aria-hidden="true" — 意味を運ぶのは読み上げられる テキストです。
  • 行は本物の <table> の 1 つの <tbody> の中の本物の <tr> の ままです — 支援技術には積み重なるテーブルではなく、1 つの育つ テーブルが見えます。
  • datagrid-pager — ページを指名(ジャンプ・共有・再開)する必要があるときの番号付き ページング。
  • lazy-panelintersect once のいとこ。オーバーフローコンテナの中で手を伸ばす べきトリガーです。
  • データグリッド — 行のレンダリング先になるコンポーネントの CSS API。