こんにちは、エキサイト株式会社の平石です。
今回は、HTMXでheadタグ内の要素を扱う方法をご紹介します。
はじめに
ソースコードはJava SpringBootを利用していますが、それ以外の言語、フレームワークでも動作するかと思います。
環境
今回のソースコードは以下の環境で動作確認をしています。
- Java 21
- SpringBoot 3.3.5
また、テンプレートエンジンとしてThymeleafを利用しています。
標準のHTMXではheadタグを扱えない
HTMXでは、HTMLタグの属性のように記述することAJAXやWebSocketsのような機能を利用できるツールです。
便利なツールなのですが、HTMX単体ではhead
タグの内容を扱うことができません。(例外的にtitle
タグは扱えます。)
例えば、以下のようなControllerとテンプレートファイルを考えます。
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("head") public class HeadSampleController { @GetMapping("sample") public String headSample() { return "head/sample"; } @GetMapping("sample2") public String headSample2() { return "head/sample2"; } }
resources/templates/head/sample.html
<!doctype html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <title>headサンプルページ</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> </head> <body> <button hx-get="/head/sample2" hx-target="body" hx-swap="innerHTML">head/sample2にリクエスト </button> </body> </html>
resources/templates/head/sample2.html
<!doctype html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <title>headサンプルページ2</title> <meta charset="UTF-8" /> </head> <body> <p>リクエスト完了</p> </body> </html>
/head/sample
にアクセスし、ボタンを押すと/head/sample2
にリクエストされbody内の要素が書き換えられます。
このとき、head
タグの内容は以下のようになっています。
- リクエスト前
<head> <title>headサンプルページ</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> <style> .htmx-indicator{opacity:0} .htmx-request .htmx-indicator{opacity:1; transition: opacity 200ms ease-in;} .htmx-request.htmx-indicator{opacity:1; transition: opacity 200ms ease-in;} </style> </head>
- リクエスト後
<head> <title>headサンプルページ2</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> <style> .htmx-indicator{opacity:0} .htmx-request .htmx-indicator{opacity:1; transition: opacity 200ms ease-in;} .htmx-request.htmx-indicator{opacity:1; transition: opacity 200ms ease-in;} </style> </head>
当然ながらhx-target="body"
としているのでhead
タグは置き換えられていません。(title
タグは例外)
では、sample.html
のbody
タグを以下のように書き換えるとどうでしょう。
<body hx-ext="head-support"> <button hx-get="/head/sample2" hx-target="html" hx-select="html" hx-swap="outerHTML">head/sample2にリクエスト </button> </body>
sample2.html
からhtml
タグを抜き出して、sample.html
のhtml
タグを置き換えようとしています。(つまり、ページ全体を置き換えます。)
この状態でボタンを押すとtitle
タグのみが変更され、それ以外のhead
タグ、body
タグの内容はそのままです。
<!doctype html> <html lang="ja" xmlns:th="http://www.thymeleaf.org"> <head> <title>headサンプルページ2</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> </head> <body> <button hx-get="/head/sample2" hx-target="html" hx-select="html" hx-swap="outerHTML">head/sample2にリクエスト </button> </body> </html>
これ以外にも、hx-target="head" hx-select="head"
としてhead
タグを置き換えようとしてもうまくいきません。
これは、HTMXが基本的にはbody
タグ(の内部)を対象としており、head
タグ内部は特定のタグ以外は無視しているからだと考えられます。
headタグを扱いたいとき
では、head
タグを扱いたいときにはどうすれば良いのでしょうか?
前の例のように、ページ全体を置き換えるのであればHTMXを使わずにHTML標準機能でリクエストを行うというのも手ですが、HTMXでリクエストをしたい場合やheadタグだけ置き換えたい場合もあるでしょう。
このような時には、htmx Head Tag Support Extension
という拡張を利用します。
利用法は簡単です。
まず、head
タグ内に<script src="https://unpkg.com/htmx-ext-head-support@2.0.2/head-support.js"></script>
を追加し、body
タグにhx-ext="head-support"
という属性を追加します。
resources/templates/head/sample.html
<head> <title>headサンプルページ</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> <script src="https://unpkg.com/htmx-ext-head-support@2.0.2/head-support.js"></script> </head> <body hx-ext="head-support"> <button hx-get="/head/sample2" hx-target="body" hx-swap="outerHTML">head/sample2にリクエスト </button> </body>
次に、head
タグを扱う際の挙動を設定します。
挙動は、hx-boost
を設定しているかどうかで変わりますが、今回は設定していない場合(デフォルトまたはhx-boost="false"
)で紹介します。
merge
リクエスト後に返すHTMLファイル(ここでは、head/sample2.html
)のhead
タグにhx-head="merge"
を指定すると、リクエスト後のhead
タグは以下のようになります。
<head> <title>headサンプルページ2</title> <meta charset="UTF-8"> </head>
この設定の場合、以下のようなアルゴリズムによって新しいhead
タグが生成されます。
- リクエスト前の
head
タグにもレスポンスのhead
タグにも含まれるタグはそのまま残る - リクエスト前の
head
タグには含まれないが、レスポンスのhead
タグに含まれるタグは末尾に追加される(<meta charset="UTF-8">
) - リクエスト前の
head
タグには含まれるが、レスポンスのhead
タグには含まれれないタグは削除される(HTMX関連のscript
タグ)
re-eval
リクエスト後(ここでは、head/sample2.html
)のhead
タグ内部の要素にhx-head="re-eval"
を指定すると、その要素はhead
タグから除去された後に再度追加されます。
これは、HTMXリクエストのたびにJavaScriptコードを実行したい場合に使えるようですが、毎回jsファイル自体のリクエストも行われるため注意が必要です。
head/sample.html
<head> <title>headサンプルページ</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> <script src="https://unpkg.com/htmx-ext-head-support@2.0.2/head-support.js"></script> <script src="/js/sample.js"></script> </head>
head/sample2.html
<head hx-head="merge"> <title>headサンプルページ2</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> <script src="https://unpkg.com/htmx-ext-head-support@2.0.2/head-support.js"></script> <script hx-head="re-eval" src="/js/sample.js"></script> </head>
js/sample.js
console.log("sample.jsをロード")
hx-preserve
リクエスト前(ここでは、head/sample.html
)のhead
タグ内部の要素にhx-preserve="true"
を指定すると、その要素はレスポンスのhead
タグに含まれていなくても除去されません。
append
リクエスト後に返すHTMLファイル(ここでは、head/sample2.html
)のhead
タグにhx-head="append"
を指定すると、リクエスト後のhead
タグは以下のようになります。
<head> <title>headサンプルページ2</title> <script src="https://unpkg.com/htmx.org@2.0.3"></script> <script src="https://unpkg.com/htmx-ext-head-support@2.0.2/head-support.js"></script> <style> .htmx-indicator{opacity:0} .htmx-request .htmx-indicator{opacity:1; transition: opacity 200ms ease-in;} .htmx-request.htmx-indicator{opacity:1; transition: opacity 200ms ease-in;} </style> <title>headサンプルページ2</title> <meta charset="UTF-8"> </head>
この設定の場合、リクエスト前のhead
タグは全て残り、レスポンスのhead
タグは全て新しいhead
タグの末尾に追加されます。
終わりに
今回は、HTMXでhead
タグ内の要素を扱う方法をご紹介しました。
実際に必要な場面は少ないかもしれませんが、知っているとHTMXでできることがより広がりそうですね。
では、また次回。