hx-get の利用


hx-get は、リンクやボタンなどの要素から GET リクエストを発行し、返ってきた HTML をページの一部に差し替えるための属性です。ページ全体のリロードを避けつつ、一覧の追加読み込み、タブ切り替え、カレンダーの月送りなどを実装できます。

a-blog cms Ver. 3.2 では標準の 組み込み JavaScript に htmx が含まれているため、hx-get を記述するだけで利用できます。詳細は「事前準備について」を参照してください。

基本構文

<a href="{url}" 
   hx-get="{url}tpl/include/entry/summary.html"
   hx-target="this"
   hx-swap="outerHTML"
   hx-push-url="{url}">リンク</a>

より実践的にイメージしやすいように a-blog cms の変数 {url} を記述して例をあげています。

通常 {url} には、リンク先となるページの URL が入ります。a-blog cms が生成する URL は、カテゴリーやエントリー、カスタムフィールドの条件などを組み合わせた URLコンテキスト と呼ばれる形式になっています。URLコンテキストとは、URLによって表現されるページの文脈情報のことで、この URL 自体が表示する内容の条件を表しています。

そして、その URL の後ろに /tpl/ を付与し、さらに include/entry/summary.html のようなテンプレートパスを指定することで、同じ条件で取得したコンテンツを、指定したレイアウトで部分的に置き換えることができます。

hx-targethx-swap では特別な設定を行っていないため、ここでの説明は省略します。

hx-push-url は、部分更新を行った際にブラウザのアドレスバーや履歴(History API)を更新するための属性です。公式ドキュメントでは hx-push-url="true" のように指定する例が紹介されていますが、この場合は hx-get に指定した URL がそのまま履歴に記録され、例えば https://example.com/tpl/entry/summary.html のように tpl パス付きの URL になってしまいます。

a-blog cms では、hx-push-url実際のページ URL(a タグのリンク先) を指定することで、htmx が使われなかった場合の通常遷移と同じ URL を履歴に残すことができます

部分更新後も履歴や URL 表示がユーザーにとって自然な形となり、戻る・進むの操作やブックマーク時の挙動も通常遷移と同様になります。


hx-get 実装例


siteテーマ「お知らせ」の一覧(もっと見る)ボタン

お知らせ一覧ページ にある、最も一般的な「もっと見る」ボタンの実装例です。

通常の Entry_Summary モジュールではページャー部分に /include/parts/pager.html をインクルードしますが、「お知らせ」の部分だけは以下の pager-read-more.html を読み込むようにしています。

/include/parts/pager-read-more.html

<!-- BEGIN pager:veil -->
<!-- BEGIN forwardLink -->
<div id="search-results">
  <div class="read-more-pager">
    <a href="{url}" class="button is-width-lg"
    hx-get="{url}tpl/include/htmx/entry/summary-more.html"
    hx-target="#search-results"
    hx-swap="outerHTML">もっと見る</a>
  </div>
</div>
<!-- END forwardLink -->
<!-- END pager:veil -->

この例では、{url} にページャーの次ページ URL が入り、その後ろに /tpl/include/htmx/entry/summary-more.html を付けて、次ページの一覧部分だけを返すテンプレートを指定しています。

hx-target="#search-results" としているため、このラッパー要素全体を hx-swap="outerHTML" で置き換えます。次ページにも同じ構造の「もっと見る」ボタンが含まれるので、連続クリックでページを進められます。


「よくある質問」のアコーディオン内の A コンテンツ読み込み

これまでは post include を利用し、質問(Q)部分に <form> タグを設置して、回答(A)を非同期で読み込んでいました。

a-blog cms Ver. 3.2 から標準搭載された htmx により、hx-get が利用できるようになったため、<a> タグでよりシンプルに読み込みが可能になりました。

変更前(post include 利用)

<form action="{url}" method="post" class="js-post_include" target="#jsIncludeTo{eid}">
  <h3 class="faq-question">
    <button type="submit" name="ACMS_POST_2GET_Ajax" class="toggle-button faq-question-button js-toggle-button" aria-expanded="false" aria-controls="answer{eid}">
      <span class="faq-icon">Q</span>
      <span class="faq-title">{title}</span>
      <span class="toggle-icon" aria-hidden="false">
        <img src="/images/arrow-down-gray.svg" width="18" height="18" alt="">
      </span>
    </button>
  </h3>
  <input type="hidden" name="tpl" value="faq/post-entry.html" />
</form>
<div id="answer{eid}" class="toggle-body js-toggle-body" aria-hidden="true">
  <div class="faq-answer">
    <p class="faq-icon"><a href="{url}">A</a></p>
    <div class="faq-body">
      <div id="jsIncludeTo{eid}"></div>
    </div>
  </div>
</div>

変更後(htmx 利用)

<a href="{url}" class="toggle-button faq-question-button js-toggle-button"
   hx-trigger="click" hx-get="{url}/tpl/include/htmx/entry/body-faq.html" 
   hx-target="#jsIncludeTo{eid}" 
   hx-swap="innerHTML">
  <span class="faq-icon">Q</span>
  <span class="faq-title">{title}</span>
</a>
<div id="answer{eid}" class="toggle-body js-toggle-body">
  <div class="faq-answer">
    <p class="faq-icon">A</p>
    <div class="faq-body">
      <div id="jsIncludeTo{eid}"></div>
    </div>
  </div>
</div>

このように hx-get を使うことで、フォーム構造を使わずにリンクだけで部分更新が実現でき、マークアップとコードの両方を簡潔にできます。


「ブログ」のカレンダーの月送り

a-blog cms の標準動作では、月送りリンクをクリックすると URL に /YYYY/MM/URLコンテキスト が付与され、サブカラムのカレンダーだけでなくメインコンテンツまでその月の表示に切り替わってしまいます。サブカラムのカレンダーだけを更新したい場合、この挙動は望ましくありません。

そこで htmx を使い、hx-get でカレンダー部分の断片だけを取得して差し替えることで、メインの表示状態を保持したままカレンダーのみを更新します。この場合には、hx-push-url 属性は利用しないようにしておきます。

blog@site/include/calendar/month.html

<!-- BEGIN_MODULE Calendar_Month id="{{module_id}}" -->
<section class="section-side" id="calendar">

<!-- ナビゲーション部分だけ -->
<a href="{url}" hx-target="#calendar" hx-get="{prevUrl}tpl/include/calendar/month.html" hx-swap="outerHTML">←</a>
<a href="{url}" hx-target="#calendar" hx-get="{nextUrl}tpl/include/calendar/month.html" hx-swap="outerHTML">→</a>

</section>
<!-- END_MODULE Calendar_Month -->

この例では、hx-get/tpl/include/calendar/month.html を指定することで、クリック時にカレンダー断片だけを取得して #calendar に差し替えています。これにより、メインコンテンツを変えずにサブカラムのカレンダーだけを更新 できます。


siteテーマ「キーワード検索」ページでのイクリメンタルサーチ

標準ではオフのため、利用する場合は site/index.html のインクルード先を次のように変更します。
変更前:@include("/include/parts/search-general.html")
変更後:@include("/include/htmx/parts/search-general.html")

この切り替えにより、フッターの キーワード検索フォーム が htmx 対応版に変わり、入力のたびに結果が更新される インクリメンタルサーチ が有効になります。

<input type="search" id="search-{{search-id}}" name="keyword" value="%{KEYWORD}" size="15" class="form-search-input" placeholder="キーワードを入力"
hx-get="/include/htmx/entry/summary-list.html" hx-trigger="keyup changed delay:500ms" hx-target="#summary_list">

フォーム本体では <form>hx-post の設定を行っていますが、インクリメンタルサーチは 入力欄のイベント によって hx-get が発火する実装です。hx-trigger="keyup changed delay:500ms" により、キー入力の確定や値の変更から 500ms 後にリクエストが送られ、#summary_list に差し替えられます。Enter での確定やボタン送信時は、従来どおりフォームの hx-post が動作し、結果表示用テンプレートに基づいた部分更新が実行されます。