htmxでSSEを簡単操作! hx-sse属性の仕組みとサンプルコード
hx-sse
属性は、Server-Sent Events (SSE) と直接やり取りするための機能を提供します。SSE は、サーバーからクライアントへリアルタイムにデータをプッシュする技術です。これにより、Web ページを更新することなく、最新の情報をユーザーに提供することができます。
属性の構文
hx-sse
属性の値は、スペースで区切られた1つ以上のキーワードで構成されます。それぞれのキーワードは以下の役割を果たします。
- swap:<eventName>: SSE メッセージの内容を、イベント名が一致するDOM ノードに差し替えます。
- connect:<url>: SSE イベントストリームのURLを指定します。
例:基本的な使用
<div hx-sse="connect:/event_stream swap:eventName">
...
</div>
上記の例では、以下の処理が行われます。
/event_stream
のURLにあるSSE イベントストリームに接続します。- イベント名が
eventName
であるメッセージを受信すると、そのメッセージの内容を<div>
要素内に差し替えます。
例:複数のイベントを処理
<div hx-sse="connect:/event_stream">
<div hx-sse="swap:eventName1">
...
</div>
<div hx-sse="swap:eventName2">
...
</div>
</div>
/event_stream
のURLにあるSSE イベントストリームに接続します。- イベント名が
eventName1
であるメッセージを受信すると、そのメッセージの内容をeventName1
要素内に差し替えます。 - イベント名が
eventName2
であるメッセージを受信すると、そのメッセージの内容をeventName2
要素内に差し替えます。
<div hx-sse="connect:/event_stream">
<button hx-trigger="sse:eventName">更新</button>
</div>
/event_stream
のURLにあるSSE イベントストリームに接続します。- イベント名が
eventName
であるメッセージを受信すると、button
要素をクリックします。
サーバーからランダムな数値をプッシュし、それをDOMに表示する
HTML
<div hx-sse="connect:/random-numbers swap:number">
<span id="number">0</span>
</div>
JavaScript (サーバー側)
const events = new EventSource('/random-numbers');
events.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
const number = data.number;
document.getElementById('number').textContent = number;
});
説明
- JavaScript コードでは、EventSource オブジェクトを使用して SSE イベントストリームに接続し、
message
イベントが発生したときに、受信したメッセージを解析して#number
要素内の数値を更新します。 - HTML コードでは、
hx-sse
属性を使用して/random-numbers
の SSE イベントストリームに接続し、イベント名がnumber
であるメッセージを受信すると、そのメッセージの内容を#number
要素内に差し替えます。
ボタンをクリックしてサーバーにイベントを送信し、サーバーからメッセージを受信してDOMに表示する
この例では、ボタンをクリックするとサーバーにイベントを送信し、サーバーから受信したメッセージを #message
要素内に表示します。
HTML
<div>
<button hx-trigger="sse:send-message">送信</button>
<span id="message"></span>
</div>
JavaScript (サーバー側)
const events = new EventSource('/events');
events.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
const message = data.message;
document.getElementById('message').textContent = message;
});
addEventListener('message', (event) => {
if (event.data === 'send-message') {
const message = 'Hello from server!';
const data = JSON.stringify({ message });
events.dispatchEvent(new MessageEvent('message', { data }));
}
});
説明
- また、
message
イベントリスナーを追加して、send-message
イベントを受信したときに、Hello from server!
というメッセージをサーバーから送信します。 - JavaScript コードでは、EventSource オブジェクトを使用して SSE イベントストリームに接続し、
message
イベントが発生したときに、受信したメッセージを解析して#message
要素内のメッセージを更新します。 - HTML コードでは、
hx-trigger
属性を使用してbutton
要素をクリックしたときにsse:send-message
イベントを送信し、#message
要素にhx-sse
属性を追加して、イベント名がmessage
であるメッセージを受信すると、そのメッセージの内容を#message
要素内に差し替えます。
この例では、シンプルなチャットアプリケーションを作成します。ユーザーはメッセージを入力して送信ボタンをクリックすると、そのメッセージがサーバーに送信され、他のユーザーにブロードキャストされます。
HTML
<div>
<input type="text" id="message-input">
<button hx-trigger="sse:send-message">送信</button>
<ul id="messages"></ul>
</div>
JavaScript (サーバー側)
const events = new EventSource('/chat');
events.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
const message = data.message;
const username = data.username;
const li = document.createElement('li');
li.textContent = `${username}: ${message}`;
document.getElementById('messages').appendChild(li);
});
addEventListener('message', (event) => {
if (event.data === 'send-message') {
const username = prompt('名前を入力してください:');
if (!username) return;
const message = document.getElementById('message-input').value;
const data = JSON.stringify({ username, message });
events.dispatchEvent(new MessageEvent('message', { data }));
}
});
- HTML コードでは、
input
要素とbutton
要素を使用して、ユーザーがメッセージを入力して送信できるようにします。また、ul
要素を使用して、受信したメッセージを表示します
Fetch API
Fetch API は、JavaScript で非同期 HTTP リクエストを処理するための標準 API です。SSE を使用するには、fetch()
関数を使用して SSE イベントストリームに接続し、addEventListener('message', ...)
を使用してイベントを処理する必要があります。
長所
- コードが柔軟で、より複雑な処理が可能になります。
- 標準 API であり、幅広くサポートされています。
短所
- htmx の他の機能との統合が難しくなる場合があります。
hx-sse
よりも記述量が多くなります。
例
const url = '/events';
fetch(url)
.then((response) => response.json())
.then((data) => {
const message = data.message;
const username = data.username;
const li = document.createElement('li');
li.textContent = `${username}: ${message}`;
document.getElementById('messages').appendChild(li);
});
WebSockets
WebSockets は、クライアントとサーバー間で双方向のリアルタイム通信を可能にする API です。SSE よりも低遅延で、双方向通信が可能という利点があります。
長所
- より複雑なアプリケーションに適している
- 低遅延で双方向のリアルタイム通信が可能
短所
- htmx との統合が難しくなる場合があります
- SSE よりも複雑で、ブラウザのサポート状況も限られている
例
const ws = new WebSocket('/events');
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
const message = data.message;
const username = data.username;
const li = document.createElement('li');
li.textContent = `${username}: ${message}`;
document.getElementById('messages').appendChild(li);
});
サードパーティ製ライブラリ
SSE を処理するためのサードパーティ製ライブラリがいくつかあります。これらのライブラリは、Fetch API や WebSockets をより簡単に使用できるようにするラッパーを提供したり、追加機能を提供したりすることがあります。
長所
- 追加機能を提供するものがある
- Fetch API や WebSockets をより簡単に使用できるようにする
短所
- htmx との統合が難しくなる場合があります
- ライブラリによって機能や使い勝手が異なる
例
ポーリング
ポーリングは、一定間隔でサーバーにリクエストを送信して、新しいデータがないかどうかを確認する手法です。SSE や WebSockets ほど効率的ではありませんが、シンプルなアプリケーションで使用できます。
長所
- コードがシンプルで、実装が簡単
短所
- リアルタイム性が低い
- 非効率で、サーバーへの負荷が高くなる
例
const url = '/events';
setInterval(() => {
fetch(url)
.then((response) => response.json())
.then((data) => {
const message = data.message;
const username = data.username;
const li = document.createElement('li');
li.textContent = `${username}: ${message}`;
document.getElementById('messages').appendChild(li);
});
}, 1000);
hx-sse
の代替方法はいくつかありますが、それぞれに長所と短所があります。最適な方法は、アプリケーションの要件によって異なります。
- htmx との統合
- ブラウザのサポート状況
- 必要なリアルタイム性
- アプリケーションの複雑性