htmxでWebSocket通信を簡単に行う「hx-ws」属性の解説
htmxは、HTML属性だけでAJAX通信やWebSocketsとの双方向通信を実現できるライブラリです。「hx-ws」属性は、このWebSockets機能を司る重要な要素の一つです。本記事では、「hx-ws」属性の役割と基本的な構文、そして具体的なユースケースを交えて、そのプログラミングについて分かりやすく解説します。
「hx-ws」属性の役割
「hx-ws」属性は、クライアントサイドからWebSocketサーバーへの接続と、サーバーとのメッセージ送受信を可能にします。具体的には、以下の機能を実現します。
- メッセージ受信
サーバーから受信したメッセージを、指定された要素に挿入したり、イベントをトリガーしたりすることができます。 - メッセージ送信
属性のサブ属性を用いて、サーバーへ送信するメッセージの内容を定義できます。 - WebSocketサーバーへの接続確立
属性値にWebSocketサーバーのURLを指定することで、クライアントとサーバー間で双方向の通信路を確立します。
基本的な構文
「hx-ws」属性の基本的な構文は以下の通りです。
hx-ws="接続先URL [オプション属性]"
- オプション属性
任意。「hx-headers」、「hx-params」などの属性を用いて、接続やメッセージングに関する詳細な設定を指定できます。 - 接続先URL
必須。WebSocketサーバーのURLを指定します。
具体的なユースケース
1 チャットアプリケーション
以下は、シンプルなチャットアプリケーションの例です。この例では、ユーザーがメッセージを入力して送信すると、そのメッセージがWebSocketサーバーに送信され、サーバーからブロードキャストされたメッセージは他のユーザーに表示されます。
<input type="text" id="message-input">
<button hx-ws="send: /chat/message" hx-headers="Content-Type: application/json" hx-target="#message-input">送信</button>
<div id="chat-messages"></div>
<script>
// メッセージ送信時の処理
const sendMessage = () => {
const message = document.getElementById('message-input').value;
const messageJSON = JSON.stringify({ message });
document.documentElement.setAttribute('hx-ws', `send: /chat/message, data: ${messageJSON}`);
};
// メッセージ受信時の処理
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.addEventListener('hx-ws-message', (event) => {
const messageData = JSON.parse(event.detail.data);
const newMessage = document.createElement('div');
newMessage.textContent = messageData.message;
document.getElementById('chat-messages').appendChild(newMessage);
});
});
</script>
2 リアルタイムな株価情報更新
以下は、WebSocketを用いてリアルタイムに株価情報を更新する例です。この例では、ユーザーが銘柄シンボルを入力すると、その銘柄の最新株価がWebSocketサーバーから受信され、ページに表示されます。
<input type="text" id="stock-symbol">
<div id="stock-price"></div>
<script>
// 銘柄シンボル変更時の処理
document.getElementById('stock-symbol').addEventListener('input', () => {
const symbol = document.getElementById('stock-symbol').value;
document.documentElement.setAttribute('hx-ws', `connect: /stocks/${symbol}`);
});
// 株価情報受信時の処理
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.addEventListener('hx-ws-message', (event) => {
const stockData = JSON.parse(event.detail.data);
document.getElementById('stock-price').textContent = `${stockData.symbol}: ${stockData.price}`;
});
});
</script>
- チャットアプリケーション
- リアルタイムな株価情報更新
- カウントアップタイマー
チャットアプリケーション
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>チャットアプリケーション</title>
<script src="https://htmx.org/docs/"></script>
</head>
<body>
<h1>チャット</h1>
<input type="text" id="message-input" placeholder="メッセージを入力">
<button hx-ws="send: /chat/message" hx-headers="Content-Type: application/json" hx-target="#message-input">送信</button>
<div id="chat-messages"></div>
<script>
// メッセージ送信時の処理
const sendMessage = () => {
const message = document.getElementById('message-input').value;
const messageJSON = JSON.stringify({ message });
document.documentElement.setAttribute('hx-ws', `send: /chat/message, data: ${messageJSON}`);
};
// メッセージ受信時の処理
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.addEventListener('hx-ws-message', (event) => {
const messageData = JSON.parse(event.detail.data);
const newMessage = document.createElement('div');
newMessage.textContent = `${messageData.user}: ${messageData.message}`;
document.getElementById('chat-messages').appendChild(newMessage);
});
});
</script>
</body>
</html>
解説
- イベントリスナー内では、受信したメッセージを解析し、DOMに新しいメッセージ要素を追加しています。
- JavaScript部分では、
sendMessage
関数を使ってメッセージを送信し、DOMContentLoaded
イベントでhx-ws-message
イベントリスナーを登録しています。 hx-target
属性を使って、メッセージ送信後にフォーカスを戻す要素を指定しています。hx-headers
属性を使って、送信するメッセージのContent-Typeをapplication/json
に設定しています。- このコードでは、WebSocketサーバーのURLを
/chat/message
に設定しています。
リアルタイムな株価情報更新
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>株価情報更新</title>
<script src="https://htmx.org/docs/"></script>
</head>
<body>
<h1>株価情報</h1>
<input type="text" id="stock-symbol" placeholder="銘柄シンボルを入力">
<div id="stock-price"></div>
<script>
// 銘柄シンボル変更時の処理
document.getElementById('stock-symbol').addEventListener('input', () => {
const symbol = document.getElementById('stock-symbol').value;
document.documentElement.setAttribute('hx-ws', `connect: /stocks/${symbol}`);
});
// 株価情報受信時の処理
document.addEventListener('DOMContentLoaded', () => {
document.documentElement.addEventListener('hx-ws-message', (event) => {
const stockData = JSON.parse(event.detail.data);
document.getElementById('stock-price').textContent = `${stockData.symbol}: ${stockData.price}`;
});
});
</script>
</body>
</html>
- JavaScript部分では、
input
イベントで銘柄シンボルが変更された時にconnect
リクエストを送信し、DOMContentLoaded
イベントでhx-ws-message
イベントリスナーを登録しています {symbol}
部分は、入力された銘柄シンボルに置き換えられます。- このコードでは、WebSocketサーバーのURLを
/stocks/{symbol}
に設定しています。
Fetch API
Fetch APIは、JavaScriptで非同期HTTPリクエストを行うための標準APIです。WebSocketとは異なり、双方向のリアルタイム通信には対応していませんが、シンプルなデータのやり取りであれば、Fetch APIの方が軽量で扱いやすい場合があります。
<button onclick="fetchStockPrice()">株価取得</button>
<div id="stock-price"></div>
<script>
function fetchStockPrice() {
const symbol = document.getElementById('stock-symbol').value;
const url = `/stocks/${symbol}`;
fetch(url)
.then(response => response.json())
.then(data => {
document.getElementById('stock-price').textContent = `${data.symbol}: ${data.price}`;
});
}
</script>
Server-Sent Events (SSE)
SSEは、サーバーからクライアントへリアルタイムにデータを送信するための技術です。WebSocketとは異なり、クライアントからサーバーへメッセージを送信することはできませんが、サーバーからの一方的なデータ配信には適しています。
<div id="stock-price"></div>
<script>
const eventSource = new EventSource('/stocks/stream');
eventSource.addEventListener('message', (event) => {
const stockData = JSON.parse(event.data);
document.getElementById('stock-price').textContent = `${stockData.symbol}: ${stockData.price}`;
});
</script>
htmx以外にも、WebSocketを扱うためのライブラリは多数存在します。例えば、以下のライブラリは、htmxよりも高度な機能や柔軟性を提供する場合があります。
選択の指針
どの方法を選択するかは、以下の要素を考慮する必要があります。
- 開発者の経験
特定のライブラリに対する経験や知識 - パフォーマンス
軽量でシンプルな方法が必要かどうか、高度な機能が必要かどうか - 必要な機能
双方向のリアルタイム通信が必要かどうか、サーバーからの一方的なデータ配信のみで十分かどうか