#困り事はタイトルの通りです。
webサイト、webツール様々開発する中でwebツール系をwebサイトの雰囲気で作っていると、この障壁にぶち当たりました。
turbolinks使用時にjavascriptが発火しないなどの記事は山ほどあったのですが、発火しすぎるという記事は少なかったので書いてみます。
##本記事を読む対象の人
- rails環境にて、turbolinksとjsを併用する時に思い通りの動きにならない。
- ページ毎に記述したいjsファイルの読み込みが呼ばれなかったり、多重にリッスンしてしまう。
- 'page:before-change'などよく分からない独特な書き方を覚えるのがめんどくさい、けどpjaxの恩恵が欲しい。
##具体的な問題
//= require jquery
//= require jquery.turbolinks
//= require rails-ujs
//= require turbolinks
//= require common
//= require cropper.min.js
//= require jquery-cropper.min.js
//などなど,treeで全ては読み込まない
<!DOCTYPE html>
<html>
<head>
<title>titles</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body class="home_body">
<%= yield %>
</body>
</html>
<div class="part_page_base">
<!-- page contents -->
</div>
<%= javascript_include_tag '各ページ.js', 'data-turbolinks-track': 'reload' %>
<!-- など、もしくは -->
<script type="text/javascript" src="/各ページ.js">
</script>
といったようにwebツールや凝ったwebサイトを作るとなるとjsの使用はほぼ必須であり、外部jsファイルを分割して各ページで呼び出しをしたくなります。の方が管理しやすくなります。
しかし、、、
window.addEventListener('turbolinks:load',async function(e) {
console.log('多重コンソールです⭐︎');
});
にしようものなら、このページを訪れたのをトリガーに他ページへ遷移しても、遷移タイミングでなぜかログが出てしまいます。
さらに、このページを訪れる度に、イベントが等差数列的に多重エンチャントされます。
まあ、pjaxってそういうものなんですけどね。
$(document).ready(async function(){
console.log('二重コンソールです⭐︎');
});
こうしたとしても、他ページではイベントはしなくなりますが、
これが一番、turbolinkが嫌われる理由ですが
ページヒストリーにページ(キャッシュ)が残っていると二重にエンチャントしてくれやがります。
確かにpjaxを自前でやろうとするとこの書き方は二重にイベント登録がされると容易に想像できます。
おしゃれなwebサイトを作ろうと、intervalで画像切り替えなどしようものならタイムスリップしてしまいます。
あの、車が右から左にしゅんしゅんしゅんしゅんと繰り返す内にパッと消えてタイムスリップする奴
こうなると各jsファイルでajax通信にてDBやりとりをしたい時などサーバーログをみてみると確かに二重で通信されています。 jsてんこ盛りアプリにturbolinksは使ってはいけないのでしょうか? ##解決に向けて いろいろ試してみました。
###いっそtubrokinksを外してしまう。
=>
当然、ちゃんと動きました。ただ、turbolinksを外した後だと遷移処理がホントに長く感じます。
共通ヘッダーもチカッと点滅してしまったり、見た目もモバイルファーストではないですね。
###application.html.erbファイルで外部読み込みjsでイベント登録。
get '/home/:unique_page_id' => 'home#index'
<input type="hidden" value="<%= @unique_page_id %>" id="page_id">
let page_id = document.getElementById('page_id').value;
if (page_id == "1") {
} else if (page_id == "2") {
} ・・・
これはpjax向け、SPA向けらしい書き方ですね。ただ、なんとなく荒々しさ丸出しな書き方な気がします。
webツールには向かなそうです。ただ、importして読み込めば一般回答に一番近い気がします。
###各ページ.html.erbに任意のinputタグを設置。
<input type="hidden" id="page_js_status">
<div class="part_page_base">
<!-- page contents -->
</div>
<script type="text/javascript" src="/各ページ.js">
</script>
if ($('#page_js_status').prop('checked') == false) {
$('#page_js_status').prop('checked',true);
$(document).ready(async function(){
console.log('一回コンソールです、やったー!⭐︎');
});
}
私はこの書き方が気に入りました。
pjaxにおいて、body要素自体は上書き的に遷移しているので
$('#page_js_status').prop('checked')はfalseに戻ります。
jsファイル自体二回読み込んでいるので、読み込み自体は倍ですが、pjaxのhtml操作は一回のみなのでpage_js_status.checked = falseが維持され、
if文内部は一度のみ実行されます。
###他、もっと完璧な方法があるという方は連絡お待ちしてます。