はじめに
某プログラミングスクールでフリマアプリの商品出品機能を実装中、JavaScriptを用いた価格連動機能において直面した不具合とその解決策を共有します。
開発環境
ruby 3.2.0
Rails 7.0.8
前提
商品の出品価格入力に連動して販売手数料(10%)と販売利益を動的に表示するJavaScript機能を実装しました。しかし、出品ページにアクセスした際に機能が働かず、ページをリフレッシュ(F5)すると正常に動作する現象に直面しました。
pin "item_price", to: "item_price.js"
import "item_price";
const price = () => {
const priceInput = document.getElementById("item-price");
priceInput.addEventListener("input", () => {
const inputValue = priceInput.value;
const price = parseFloat(inputValue);
if (!isNaN(price) && price >= 300 && price <= 9999999) {
const fee = Math.floor(price * 0.1);
const profit = Math.floor(price - fee);
document.getElementById("add-tax-price").innerHTML = fee.toLocaleString();
document.getElementById("profit").innerHTML = profit.toLocaleString();
} else {
document.getElementById("add-tax-price").innerHTML = "";
document.getElementById("profit").innerHTML = "";
}
});
};
window.addEventListener("turbo:load", price);
何が原因?
Turbolinksが非同期通信がうまくいかない原因であることが判明しました。
Turbolinksとは?
Turbolinksは、Ruby on Railsで広く使用されるJavaScriptライブラリで、ページの読み込み速度を向上させることを目的としています。
通常、ページ遷移は新しいページをサーバーから取得し、現在のページを破棄して新しいページを表示するプロセスを伴います。しかし、Turbolinksを使用すると、フルページの再読み込みを行わずに、サーバーから新しいページのHTMLのみを取得し、現在のページのbodyタグの中身を新しいHTMLで置き換えます。CSSやJavaScriptは再読み込みされずに保持されるため、ページの切り替えが非常に速くなります。
どう解決したのか
Turbolinksを無効にするため、非同期通信を行いたいページへのリンクとフォームにdata: { turbo: false }を追加しました。
<%= link_to new_item_path, data: { turbo: false },class: 'purchase-btn' do %>
<%= form_with model: @item, data: { turbo: false }, local: true do |f| %>
原因についての詳細
Turbolinksは、ページ遷移を高速化するためにページを完全には再読み込みせず、bodyタグの中身とページのタイトルだけを交換する仕組みです。これにより、ページの遷移が速くなりますが、JavaScriptが正しく動作しない場合があります。
通常のウェブページでは、ページが読み込まれるときにwindowオブジェクトのloadイベントが発火し、これをトリガーにJavaScriptのコードが実行されます。しかし、Turbolinksを使用している場合、ページ遷移時にはwindowのloadイベントは発火しません。その代わりに、Turbolinksは独自のイベントturbo:loadが使用されるようです。
今回のケースは、Turbolinksが原因でloadイベントが発火しないため、JavaScriptが初期化されずに機能しないというものというふうに解釈しています。ページを更新(F5)すると機能するようになるのは、Turbolinksは使われずに通常のページ読み込みが行われるため、loadイベントが発火し、JavaScriptが期待通りに動作するからと推察できます。