記事の対象者
Railsアプリにおいて、jQueryを使用して投稿を分けて表示させるタブを実装。
実装したはいいが、リロードしないと正常に動作しない問題が発生、、。
Turbolinksはそのままに、解決する方法とその理由を示したいと思います。
他の記事で解決策はあったものの、「なぜ?」が解消されず附に落ちなかった為、
Rails定番の参考書「現場Rails」の引用と独り言を添えて、初学者向けの記事にしてみました。
初学者の為、解釈が間違っていれば、ご指摘頂けると幸いです。
リロードしないと正常に動作しないタブ部分のjsファイルのコード
$(function() {
let tabs = $(".menu-tab");
$(".menu-tab").on("click", function() {
.
(省略)
.
})
コードは間違ってないはずだが、なぜかタブの切り替えができない。
他の記事で以下の解決策が示されていました。
##解決方法
以下のように、document.addEventListener("turbolinks:load", function() {})を加える。
document.addEventListener("turbolinks:load", function() {
$(function() {
let tabs = $(".menu-tab");
$(".menu-tab").on("click", function() {
.
(省略)
.
})
リロードせずともタブの切り替えができ、タブごとの投稿を表示させることができた。解決。
原因は、turbolinksによりイベントが実行されないらしい。。ん??
初学者でない方は、おそらくこれで説明されて理解できるが、初学者の私はあまり附に落ちない。(turbolinksを理解していれば附に落ちますが、私は理解しておりませんでした。)
以下、Rails定番の参考書「現場Rails」のTurbolinks部分の説明を引用しながら、上の「turbolinksによりイベントが実行されない」原因を理解します。
「turbolinksによりイベントが実行されない」理由
まず、turbolinksとは?
Railsが提供するJavaScriptの中には、ページ遷移を高速化する仕組みもあります。それがTurbolinksです。Turbolinksはすべてのリンククリックに対するページ遷移を自動的にAjaxすることで高速化を図ります。
仕組みを簡単に説明すると、Turbolinksはa要素のクリックイベントをフックして、遷移先のページをAjaxで取得します。そして、取得したページが要求するJavaScriptやCSSが現在のものと同一であれば現在のものをそのまま利用し、title要素やbody要素のみを置き換えます。リクエストごとにJavaScriptやCSSをブラウザが評価しなくなるため、パフォーマンスを向上させることができるのです。title要素やbody要素のみを置き換えるだけなので、ページ遷移は発生しませんが、...
引用:現場で使えるRuby on Rails 速習実践ガイドp340
高速化を図る為の仕組みで、ページ遷移を発生させず置き換えて表示させる仕組みということか。ふーん。
turbolinks:loadとは?
Turbolinksは、ページ遷移や戻る、進むといったブラウザの動作をフックして動作する形になるため、ブラウザが用意しているイベントだけでは、意図したとおりの処理を実現できないことがあります。
そのため、 Turbolinksは自身の処理状態に応じてイベントを発行するようになっています。例えば、「turbolinks:load」イベントは初回のページ表示時やTurbolinksによりページの状態が遷移した際に発火するイベントです。
引用:現場で使えるRuby on Rails 速習実践ガイドp342
ブラウザが用意しているイベントでは実現できない場合は、自身の処理状態に応じてイベントを発行させる記述が必要ということか。「turbolinks:load」は、その例でこれを記述することによって、ページ表示・遷移時(=処理状態)にイベントが発火できるということか🤔
今回の場合は、ブラウザが用意しているイベントでは実現できないのだな。
8-3 2-1 ブラウザのページ遷移が発生しないことが多くなる
「ページが表示される際、DOMの用意ができたタイミングで何らかの処理を実行したい」といったことはよくあります。(省略)...jQueryの「$(document).ready()」がありますが、Tubolinksによるページ遷移の場合はうまく機能しません。なぜなら実際にはブラウザのページ遷移が発生しておらず、イベントが発火しない為です。
引用:現場で使えるRuby on Rails 速習実践ガイドp342
上でいう、何らかの処理(jQueryの「$(document).ready())は今回のケースに合致するな。。
お!ここに「実際にはブラウザのページ遷移が発生しておらずイベントが発火しない」と書かれていますね!!
今回の場合も、ページ遷移が発生しない結果イベントが発火しないから、表示できなかったわけですね。
たとえば、Turbolinks環境下において、次のJavascriptコードが読み込まれる画面があるとしましょう。この画面を、ブラウザ(タブ)で、このアプリケーション内で最初に開くページとしてアクセスするならば、この処理は実行され、「Hello,World」と表示されます。しかし、アプリケーションの別の画面のリンクを辿って到達した画面にこのスクリプトがあっても実行されないのです。
引用:現場で使えるRuby on Rails 速習実践ガイドp342
windows.onload = function(){
console.log('Hello World!');
};
確かに最初からタブのページを表示させた場合は、動作したけれど、別の画面からタブのページにきた時はリロードしないと表示できなかった!!この状況だったんです、先生!!
この問題は、前述したTurbolinksが提供するイベントturbolinks:loadを利用することで解決します。
引用:現場で使えるRuby on Rails 速習実践ガイドp343
document.addEventListener("turbolinks:load", function() {
windows.onload = function(){
console.log('Hello World!');
});
上で挙げた解決法と同じことを言っていますね!!
逆に言えば、Turbolinksを利用している場合は、このようなTurbolinks専用のイベントを使わないとJavaScriptを正しく動かすことができないということです。つまり、Turbolinksに依存したJavaScriptを書くことになります。
引用:現場で使えるRuby on Rails 速習実践ガイドp343
最後に結論が書いてありますね。
「Turbolinksを利用している場合は、このようなTurbolinks専用のイベントを使わないとJavaScriptを正しく動かすことができない」
解決方法にあげたコードを追加する理由はこれですね。以上です。