htmx
最近htmxというものをAIに教えてもらい、使い方を勉強しています。
after-swapイベントの捕捉
これをどこに設定するとイベントを捕捉できるのかを調べました。
index1.html
<html>
<head>
<script src="https://unpkg.com/htmx.org@2.0.8"></script>
</head>
<body>
<button
hx-get="content1.html" hx-target="#div1"
hx-swap="innerHTML"
hx-on::after-swap="console.log('after swap at button1');"
>
innerHTML
</button>
<div id="div1" style="border: 4px ridge blue; padding: 0.5rem;"
hx-on::after-swap="console.log('after swap at original #div1');"
>
original content
</div>
<button
hx-get="content2.html" hx-target="#div2"
hx-swap="outerHTML"
hx-on::after-swap="console.log('after swap at button2');"
>
outerHTML
</button>
<div id="div2" style="border: 4px ridge blue; padding: 0.5rem;"
hx-on::after-swap="console.log('after swap at original #div2');"
>
original content
</div>
</body>
</html>
content1.html
<div id="div1-1" style="border: 4px ridge red;"
hx-on::after-swap="console.log('after swap at #div1 in content1.html');"
>
content
</div>
content2.html
<div id="div2" style="border: 4px ridge red;"
hx-on::after-swap="console.log('after swap at #div2 in content2.html');"
>
content
</div>
ブラウザーの開発者ツールのコンソールを開いて確認します。
結果は、swapをinnerHTMLにして、targetに設定しないとだめということです。outerHTMLだと、イベントリスナーが設定されているtargetが消えるので、after-swapイベントが捉えられません。また、新しいtargetの要素はまだhtmxによってイベントリスナーが設定されていないので、こちらもだめということです。
どうしてもouterHTMLでやりたいときにどうしたらいいのかというと、targetの親要素(今回はbody)にイベントリスナーを設定する(hx-on::after-swapをつける)のがよいようです。
index2.html
<html>
<head>
<script src="https://unpkg.com/htmx.org@2.0.8"></script>
<script><!--
function myLog(evt){
console.log(evt.target);
}
--></script>
</head>
<body
hx-on::after-swap="myLog(event);"
>
<button
hx-get="content1.html" hx-target="#div1"
hx-swap="innerHTML"
>
innerHTML
</button>
<div id="div1" style="border: 4px ridge blue; padding: 0.5rem;">
original content
</div>
<button
hx-get="content2.html" hx-target="#div2"
hx-swap="outerHTML"
>
outerHTML
</button>
<div id="div2" style="border: 4px ridge blue; padding: 0.5rem;">
original content
</div>
</body>
</html>
innerHTMLをクリックするとdiv#div1が、outerHTMLをクリックするとdiv#div2が示されました。
hx-push-urlを使ったURLの履歴変更
htmxを使ってタブ切り替えをつくりました。タブをクリックするとそのページの内容をhx-getで取得するようにしました。
そのとき、ページのリロード(F5)をすると、最初に読み込むページに戻ってしまい、開いたタブを再現できなくなるのが不都合でした。
そこで、クリックしたところ(ハッシュ)をURLに表示するようにして、ページリロードでそれをクリックすることを思いつきました。
ページがロード済みの場合はhx-getを無効にしつつ、URLの変遷だけはできないかといろいろと試してみました。
index3.html
<html>
<head>
<script src="https://unpkg.com/htmx.org@2.0.8"></script>
</head>
<body>
<table>
<caption>hx-get, href, hx-push-urlの動作確認</caption>
<thead>
<tr>
<th></th><th colspan=3>値</th><th colspan=3>結果</th>
</tr>
<tr>
<th></th>
<th>href</th><th>hx-get</th><th>hx-push-url</th>
<th>Jump</th><th>hx-get</th><th>hx-push-url</th>
</tr>
</thead>
<tbody>
<tr>
<th><a
href='#jump1'
hx-get="content.php?jump_to_valid_href"
hx-target="#div1"
hx-push-url="#link1"
>Link1</a></th>
<td>#jump1</td>
<td>content.php</td>
<td>#link1</td>
<td>○</td>
<td>○</td>
<td>○</td>
</tr>
<tr>
<th><a
href=''
hx-get="content.php?not_jump"
hx-target="#div1"
hx-push-url="#link2"
>Link2</a></th>
<td>空</td>
<td>content.php</td>
<td>#link2</td>
<td>×</td>
<td>○</td>
<td>○</td>
</tr>
<tr>
<th><a
href=''
hx-get=''
hx-target="#div1"
hx-push-url="#link3"
>Link3</a></th>
<td>空</td>
<td>空</td>
<td>#link3</td>
<td>×</td>
<td>index3.html</td>
<td>○</td>
</tr>
<tr>
<th><a
href=''
hx-target="#div1"
hx-push-url="#link4"
>Link4</a></th>
<td>空</td>
<td>なし</td>
<td>#link4</td>
<td>×</td>
<td>○</td>
<td>×</td>
<td>same to reload</td>
</tr>
<tr>
<th><a
hx-get='content.php?link5_was_clicked'
hx-target="#div1"
hx-push-url="#link5"
>Link5</a></th>
<td>なし</td>
<td>content.php</td>
<td>#link5</td>
<td>×</td>
<td>○</td>
<td>○</td>
<td>not link</td>
</tr>
<tr>
<th><a
hx-target="#div1"
hx-push-url="#link6"
>Link6</a></th>
<td>なし</td>
<td>なし</td>
<td>#link6</td>
<td>×</td>
<td>×</td>
<td>×</td>
<td>何も起きない</td>
</tr>
</tbody>
</table>
<div id="div1" style="border: 4px ridge blue; padding: 0.5rem;">
original content
</div>
<div style="height: 300px">
<span id='jump1'>Jump1</span>
</div>
</body>
</html>
結論はhx-getが成功しないとhx-push-urlは動かない(by ChatGPT)。
私の用途にはhx-push-urlは使えないということで、hx-on:click="history.pushState({}, ", 'link’);とするより他なさそうです。