問題点
Railsアプリケーションにて、Turboリンクを利用してページの一部を動的に更新する際、バリデーションに引っかかった後の画面ではJavaScriptのイベントが発火しなくなる問題が発生しました。具体的には、チェックボックスの状態変更によって発火するはずのイベントが動作しないという事象です。
原因
Turboリンクはページの完全な再読み込みを避け、高速なページ遷移を実現しますが、これによりページ全体が新しく読み込まれるわけではないため、従来のDOMContentLoaded
イベントを使用したJavaScriptの初期化が適切に行われません。このため、新しく追加されたDOM要素へのイベントバインドが失われることが原因です。
解決方法
この問題を解決するためには、Turboリンクが提供する特定のイベントに対してリスナーを設定し、これらのイベントが発生するたびにイベントリスナーを再設定する必要があります。これにより、ページの部分的な更新後もイベントリスナーが保持され、機能が維持されます。
具体的な実装
以下は、turbo:load
, turbo:frame-load
, および turbo:render
イベントにリスナーを設定し、それぞれのイベント発生時に必要なJavaScript機能を再初期化する方法です。
document.addEventListener('turbo:load', setupEventListeners);
document.addEventListener('turbo:frame-load', setupEventListeners);
document.addEventListener('turbo:render', setupEventListeners);
function setupEventListeners() {
const businessHoursTable = document.getElementById('business-hours-table');
document.querySelectorAll('input[name="company[business_day_ids][]"][type="checkbox"]').forEach(checkbox => {
checkbox.removeEventListener('change', updateBusinessHours);
checkbox.addEventListener('change', updateBusinessHours);
});
updateBusinessHours(); // 初回読み込み時にも実行
}
function updateBusinessHours() {
const selectedDays = Array.from(document.querySelectorAll('input[name="company[business_day_ids][]"][type="checkbox"]:checked')).map(checkbox => checkbox.value);
fetch('/companies/update_business_hours', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({ selected_days: selectedDays })
})
.then(response => response.text())
.then(data => {
businessHoursTable.innerHTML = data;
})
.catch(error => console.error('Error:', error));
}
注意点
-
removeEventListener
を使って既存のイベントリスナーを除去することで、イベントが重複して登録されるのを防ぎます。 -
turbo:frame-load
やturbo:render
など、ページ更新のコンテキストに合わせたイベントを選択してください。
この方法を用いることで、Turboリンクを使用してもJavaScriptのイベントが正しく機能するようになります。