複数のエリアの更新


通常、htmx では hx-target に指定した 1 つの要素だけが更新されます。しかし、hx-swap-oob 属性を利用すると、メインの差し替え対象とは別の要素も同時に更新することができます。これにより、1 回のリクエストで複数のエリアを並行して差し替えられるため、メインコンテンツ更新と同時に件数表示、通知領域、タイトル、ナビゲーションなどをまとめて更新することが可能になります。

レスポンス側の HTML に、更新対象と同じ id を持つ要素を用意し、その要素に hx-swap-oob を付与するだけで、メイン更新後にアウト・オブ・バンド(OOB)で差し替え処理が行われます。


基本構文

通常、htmx では hx-target に指定した 1 つの要素だけが更新されます。例えば次のような構造の場合、クリック時に更新されるのは id="summary_result" のみです。

<nav id="topicpath">
 <!-- パンくずリスト -->
</nav>

<main>
 <div id="summary_result">
  <!-- エントリー一覧など -->
 </div>

 <a href="{url}" hx-get="{url}/tpl/htmx/include/entry_summary.html"
            hx-target="#summary_result">クリック</a>
</main>

しかし、hx-swap-oob 属性を使うと、メイン差し替え対象とは別の場所にある要素、ここでは id="topicpath" も同時に更新できます。これにより、一覧部分の差し替えと同時にパンくずリストや件数表示、タイトルなど複数箇所をまとめて更新することが可能になります。

呼び出される entry_summary.html

<div id="summary_result">
 <!-- エントリー一覧など -->
</div>

<!-- 通常は上記まで -->

<nav id="topicpath" hx-swap-oob="true">
 <!-- パンくずリスト -->
</nav>

通常の部分更新では、hx-target="#summary_result" の指定により #summary_result だけが差し替えられます。しかし、レスポンス側に id="topicpath" の要素を追加し、hx-swap-oob="true" を付与することで、ページ内の該当要素も同時に更新されます。

ポイント

id の一致が必須

ページ側とレスポンス側の id が同じである必要があります。ここでは topicpath が一致しているため、OOB 更新が適用されます。

true は outerHTML 相当

hx-swap-oob="true" は、その要素ごと(outerHTML)置き換える動作になります。中身だけを差し替えたい場合は、hx-swap-oob="innerHTML" のように指定します。

複数併用可能

1 回のレスポンス内に、複数の hx-swap-oob 要素を含めて複数エリアを同時更新できます。 hx-swap-oob 属性の無い部分、上記であれば <!-- 通常は上記まで --> も含め hx-target のエリアに更新されます。も


hx-swap-oob 実装例

siteテーマの「事例紹介」の検索

https://utsuwa3m.ablogcms.org/works/htmx.html#works-search

以下は、a-blog cms で事例紹介の一覧(summary-works.html)をメイン更新しながら、同時にパンくずリスト(topicpath.html)や <title> を置き換える例です。

htmx.html ( 通常は index.html )

  <!-- サマリー:事例紹介一覧、検索条件ボックス -->
  @include("/include/htmx/summary-works.html", {"multi_swap": "off"})

  <!-- 条件から検索する -->
  @include("/include/htmx/search-works.html")

summary-works.html をインクルードする際に、変数 {{multi_swap}}off を渡しています。

summary-works.html

<!-- BEGIN_MODULE Entry_Summary id="summary_works" -->
<div id="summary_works" data-acms-hx-push-url="true">
 <!-- エントリー一覧 -->
</div>
<!-- END_MODULE Entry_Summary -->

<!-- BEGIN_IF [{{multi_swap}}/neq/off] -->

 @include("/include/htmx/topicpath.html")

 <!-- BEGIN_MODULE Ogp id="ogp" -->
 <title>{title}</title>
 <!-- END_MODULE Ogp -->

<!-- END_IF -->

通常表示では IF ブロック内は出力されませんが、htmx で呼び出されるときだけ topicpath.html<title> を含めて返します。このように IF ブロックで分岐することで、通常表示用の summary-works.html と htmx 用の summary-works.html を共通化 でき、テンプレートの重複を避けることができます。

<title> タグは hx-swap-oob 属性を付けなくても置き換えられるため、OOB 更新に合わせて記述しておくとタイトルも更新されます。

topicpath.html

<!-- BEGIN_MODULE Topicpath id="topicpath" -->
<nav class="topicpath" aria-label="現在位置" id="topicpath" hx-swap-oob="true">
 <!-- パンクくずリスト -->
</nav>
<!-- END_MODULE Topicpath -->

hx-swap-oob="true" により、#summary_works の更新時にパンくずリストも同時に置き換えられます。

この仕組みで、一覧とナビゲーションの整合 を 1 回のリクエストで保つことができます。