htmx の基本
htmx とは
htmx は、HTML の属性だけで Ajax や部分更新、履歴管理といった高度なインタラクションを実現できる軽量な JavaScript ライブラリです。JavaScript コードをほとんど書かずに、既存の HTML 要素に hx-
から始まる属性を付与するだけで機能を追加できるのが特徴です。
a-blog cms には Ver. 1.3(2011年リリース)から、POST
リクエストで部分的に HTML を取得・表示できる post include 機能が存在しました。この機能により、ページの一部だけをサーバーから取得して差し替える仕組みを長く提供してきましたが、利用できるのは POST
メソッドのみでした。
今回 a-blog cms Ver. 3.2 からは、この post include の発想を引き継ぎつつ、より柔軟でオープンな標準技術である htmx を採用しました。htmx では hx-post
だけでなく、hx-get
をはじめとする多様な HTTP メソッドを利用でき、より多くのユースケースに対応できます。これにより、長年推奨してきた post include 機能から、モダンで汎用的な htmx への移行を推奨しています。
主な特徴
シンプルな記述:
hx-get
やhx-post
などの属性を HTML に追加するだけで、非同期通信やコンテンツの差し替えが可能です。サーバーサイドと相性が良い: 部分的な HTML(フラグメント)をサーバーから返すことで、複雑な JavaScript フレームワークを使わずに SPA ライクな体験を実現できます。
プログレッシブエンハンスメント: htmx を使ったページは、JavaScript が無効でも通常のリンクやフォームとして動作します。
基本構文の例
リンクで部分更新 : hx-get
<a href="/news/page/2"
hx-get="/news/page/2/tpl/include/entry/news-list.html"
hx-target="#news-list"
hx-swap="innerHTML">
もっと見る
</a>
<ul id="news-list">
<!-- モジュールで出力された記事一覧 -->
</ul>
この例では、リンクをクリックすると /news/page/2
に GET
リクエストを送り、 include/entry/news-list.html
のテンプレートを利用し、news
のカテゴリーの page/2
の結果の HTML を #news-list
の中身に置き換えます。ページ全体のリロードは行われません。
フォーム送信で部分更新 : hx-post
<form hx-post=""
hx-target="#search-result"
hx-swap="outerHTML">
<input type="hidden" name="tpl" value="/include/entry/search-result.html">
<input type="text" name="keyword" placeholder="キーワード">
<button type="submit" name="ACMS_POST_2GET_Ajax">検索</button>
</form>
<div id="search-result">
<!-- 送信結果がここに表示される -->
</div>
この例では、フォーム送信時に POST
リクエストをサーバーに送り、include/entry/search-result.html
のテンプレートを利用し、キーワード検索結果の HTMLを #search-result
全体と置き換えます。
事前準備について
Ver. 3.2 からは不要に
a-blog cms Ver. 3.2 では、標準の 組み込み JavaScript に htmx が含まれるようになりました。これにより、HTML 要素に hx-get
や hx-post
の属性を記述するだけで自動的にライブラリが読み込まれるため、事前の設定は必要ありません。
なお、JavaScript などで htmx の属性を後から付加する場合、ライブラリが読み込まれないタイミングが発生する可能性があります。そのような場合は、以下の <meta>
タグをあらかじめ入れておくことで対応できます。
<meta name="acms-htmx" content="enable">
Ver. 3.1 までの場合
Ver. 3.1 までは、htmx.org のドキュメントに沿って <head>
内でライブラリを読み込み、設定を記述する必要があります。例えば以下のような準備が必要でした。参考として共有しておきます。
<script src="/js/htmx.mim.js"></script>
<script>
htmx.config.historyCacheSize = -1;
htmx.config.refreshOnHistoryMiss = true;
addEventListener('htmx:beforeHistoryUpdate', function (event) {
const proposedUrl = event.detail.history.path;
let customUrl = proposedUrl;
if (proposedUrl.includes('/include/htmx/')) {
customUrl = proposedUrl.replace(/\/include\/htmx\/.*\.html/, '');
}
event.detail.history.path = customUrl;
});
document.addEventListener("htmx:configRequest", function(event) {
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
event.detail.headers['X-CSRF-Token'] = csrfToken;
});
addEventListener('htmx:afterSwap', function (event) {
ACMS.Dispatch(event.target);
});
</script>