🎯 やりたかったことは?
JSFのAjaxで画面の一部を更新したあと、
その部分に対してJavaScriptの処理も使いたい場面がありました。
例えば、
- クリックイベントを登録する
- 入力項目の状態を初期化する
- JavaScript側でDOMを取得する
- プラグインや独自UIを初期化する
といった処理です。
最初の画面表示では問題なく動いていても、
Ajaxで再描画したあとだけ動かなくなることがあります。
JSFのAjaxではDOMが差し替わることがあるため、
JavaScript側の初期化も再度必要になるケースがあります。
🚀 急いでいる人向け
JSFのAjax後にJavaScriptの処理が効かなくなった場合は、
Ajaxのrenderで対象DOMが差し替わっていないか
を確認します。
DOMが差し替わった場合、
古いDOMに対して行った
element.addEventListener(...);
などの設定は新しいDOMには引き継がれません。
Ajax完了後に、
function ajaxSuccess(event) {
if (event.status !== "success") {
return;
}
initialize();
}
のように再初期化します。
💡 この記事で分かること
- JSFのAjax後にJavaScriptが効かなくなる理由
-
renderによるDOM差し替えの影響 - Ajax完了後に初期化処理を呼ぶ方法
- 初期化処理を関数にまとめる理由
- イベントの二重登録を防ぐ考え方
はじめに
JSFで画面を作っていると、
サーバー側のAjax処理とJavaScriptを組み合わせることがあります。
例えば、
const button = document.getElementById("sampleButton");
button.addEventListener("click", () => {
// 処理
});
のようなイベント登録です。
初期表示では普通に動きます。
ただ、JSFのAjaxでそのボタンを含む領域を render すると、
見た目は同じなのにイベントだけ動かなくなることがあります。
自分も最初は、
JavaScript側の処理が途中で止まった?
と思っていました。
実際にはJavaScriptの問題ではなく、
Ajaxによって対象のDOMそのものが入れ替わっていました。
🔍 なぜ再初期化が必要になるのか
例えば、最初の画面に次のボタンがあるとします。
<button id="sampleButton">
実行
</button>
JavaScriptから、
const button = document.getElementById("sampleButton");
button.addEventListener("click", () => {
console.log("click");
});
を登録します。
この時点では、
DOM A
└─ clickイベントあり
という状態です。
その後、JSFのAjaxでこの領域を再描画します。
<f:ajax
execute="@this"
render="buttonArea" />
すると、ブラウザ上では対象DOMが置き換えられます。
DOM A
↓ 削除
DOM B
↓ 新しく生成
見た目:同じ
id:同じ
イベント:なし
新しく生成されたDOM Bには、
DOM Aへ登録したイベントは存在しません。
✅ 初期化処理を関数にまとめる
こういう処理は、
最初から初期化関数としてまとめておくと扱いやすいです。
function initialize() {
const button =
document.getElementById("sampleButton");
if (!button) {
return;
}
button.addEventListener("click", () => {
console.log("click");
});
}
初期表示時に、
initialize();
を呼びます。
Ajax後にも同じ関数を呼びます。
Ajax完了後に再初期化する
JSF Ajaxの完了後に処理を入れる場合は、
Ajaxのステータスを確認してから初期化します。
例えば、
ajaxSuccess(event) {
if (event.status !== "success") {
return;
}
this.initialize();
}
のようにします。
実際には、
すべてのAjaxで毎回呼ぶのではなく、
対象のDOMを再描画したときだけ実行する方が安全です。
ajaxSuccess(event) {
if (event.status !== "success") {
return;
}
switch (event.source.id) {
case "samplePage_saveAction":
this.initialize();
break;
default:
break;
}
}
⚠️ 何でも再初期化すればいいわけではない
ここで注意したいのが、
イベントの二重登録です。
例えば同じDOMに対して、
button.addEventListener("click", () => {
console.log("click");
});
を2回実行すると、
クリック時の処理も2回実行されます。
click
click
となります。
AjaxでDOMが置き換わった場合は問題ありません。
新しいDOMなので、
イベントを改めて登録する必要があります。
一方、DOMが置き換わっていないのに
毎回 initialize() を呼ぶと二重登録の原因になります。
💼 実務での使い分け
自分の場合は、
Ajaxでrenderした領域
↓
その中にJavaScriptで初期化している要素があるか
を確認するようにしています。
対象がある場合だけ、
initialize();
を呼びます。
特に確認するのは、
addEventListener- DOM取得結果を変数に保持している処理
- JavaScriptで状態を付与している要素
- 外部ライブラリやUI部品の初期化
あたりです。
逆に、
Ajaxの render 対象外なら再初期化する必要はありません。
📝 まとめ
JSFのAjax後にJavaScriptが動かなくなった場合は、
まずDOMが再描画されていないか確認します。
-
renderされたDOMは新しいDOMへ置き換わる - 古いDOMへ登録したイベントは引き継がれない
- JavaScriptの初期化処理は関数にまとめておくと使いやすい
- Ajax完了後に必要な場合だけ再初期化する
- DOMが置き換わっていない場合は二重登録に注意する
JSFでは自分でDOMを書き換えた感覚がなくても、
f:ajax render によってDOMが変わっていることがあります。
Ajax後だけJavaScriptの挙動がおかしい場合は、
再描画範囲も確認すると原因を追いやすいです。