【実践して学ぶ！】RailsのTurbolinks利用でもJavaScriptが使用したい時にすること３選

どうも、三町哲平です！

Ruby on Railsで、アプリを開発中に導入したはずのJavaScriptが反応しない！何でなの！？という経験は、誰にでもあることかと思います。

１回目のサイト表示は上手くいくのに次にリンクをクリックしても表示しない...。キャッシュが原因なのだろうか？など、疑問が生まれた方も数多くいらっしゃるのではないのでしょうか？

そうした疑問点を抱いた中、調べていくとターボリンクなる機能に行き着きました。

てな訳で、今回は自分自身の理解を深める為にもRails初心者の方になるべく分かりやすい様に記事を書きましたので、よろしくお願いします。

※この記事は、Rails6を使用していますが、標準であるWebpackerではなくsprocketsを使用しています。

# turbolinksとは？
そもそもturbolinkとは何なのか？
> Turbolinksはリンクを生成する要素であるa要素のクリックをフックにして、
移動した先のページをAjaxで取得します。
その後、取得ページのデータが遷移前のページと同じものがあれば再利用し、
title・body要素を置き換えて表示します。
データを再利用するので、アプリケーションの速度向上などのパフォーマンスの向上を
させることができます。
引用元:[Railsにおけるturbolinksの無効化の手順 - その辺にいるWebエンジニアの備忘録](https://kossy-web-engineer.hatenablog.com/entry/2018/11/29/093958)

ページ遷移をした時に変化しない、HTML部分はそのまま再利用しましょうね！という事です。
つまりこのturbolinksのおかげで、Railsをより高速に使用することが出来るようになった訳なんですね。

Rails4以降は、標準で組み込まれているライブラリであり、__Asset Pipeline__という仕組みの一部になります。

__↓更に詳細に__
> 「Turbolinks」は、ページを遷移する際に、現在のページの [title] と [body] だけを抜き取って、新しい HTML の [title]と [body] に交換します。
こうすることで、 内で参照されるスタイルシートやJavascript をブラウザが取得しなおす処理をスキップし、更に通信を抑えることが出来る様になったのです。
引用元:[『Turbolinks』って、なんぞ？ - ryotaku's Tech Blog](https://www.ryotaku.com/entry/2019/01/15/213420)

# JavaScriptを使用したい時にどうする？
Turbolinksが画面遷移を効率化している事は、分かりましたがそのせいでJavaScriptの挙動がおかしくなってしまっては、元も子もありません。

てな訳で、ここからは、解決方法を紹介します。

ここからの参考及び引用させていただく記事は、[Rails Turbolinksをページ毎に無効化する方法 - Qiita](https://qiita.com/keitah/items/05f02efe4e11ab2473e8)になります。

では、順を追って解決策を3つご紹介致します。

## 1.JavaScriptの記述を変更する。

__jQueryを利用の場合__

```js:app/assets/javascripts/application.js
//初回読み込み、リロード、ページ切り替えで動く。
$(document).on('turbolinks:load', function() { });

//初回読み込み、ページ切り替えで動く。リロードは動かない
$(document).on('turbolinks:render', function() {　});

//ページ遷移前に行いたい処理用。ページ切り替えでもリロードでも動かない
$(document).on('turbolinks:request-start', function() {　});

```

このやり方が、特定のJQueryのソースコード毎の設定になるので、一番安全かつエラーの出にくいやり方になるのでTurbolinksの機能を損なわない一番理想系になると思われます。

## 2.view(.html.erb)のリンク毎に変更する。

```:app/views/layouts/application.html.erb
<%= link_to "お問い合わせ", inquiry_index_path %>
```
```:app/views/layouts/application.html.erb
<%= link_to "お問い合わせ",inquiry_index_path, data: {"turbolinks"=>false} %>
```
このようにリンクに__data: {"turbolinks"=>false}_を追加することで、そのリンクでの画面遷移でのturbolinksの機能が使用されない様になります。

しかし、この場合だと本来読み込む必要のないHTMLの[title]と[body]まで再読み込みされてしまうので、出来るだけ__1.JavaScriptの記述を変更する。__を使用することを個人的におすすめします。

あと、私もRails開発当初は、__2.view(.html.erb)のリンク毎に変更する。__の内容を採用していましたが、viewの一つ一つのリンクに__data: {"turbolinks"=>false}__のつけ忘れが無いかのチェックなど余計な手間が増えるので、仕様上どうしても必要な時に使用するくらいに留めておくのが良いと思います。

## 3.全部消す

大元の __gem 'turbolinks'__ を削除します。

```ruby:Gemfile
# Use jquery as the JavaScript library 

 gem 'jquery-rails' 
 # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 
 gem 'turbolinks', '~> 5' ←　ここをコメントアウト
 # gem 'turbolinks', '~> 5' 
 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 
 gem 'jbuilder', '~> 2.5' 
# Use Redis adapter to run Action Cable in production 
```

__application.js__

```js:app/assets/javascripts/application.js
//= require jquery 

//= require jquery_ujs 
//= require turbolinks ←　ここを削除
//= require owl.carousel 

//= require_tree . 

```

__application.html.erb__

```:app/views/layouts/application.html.erb
<head>

--省略--

  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

--省略--

</head>
```
```:app/views/layouts/application.html.erb
<head>

--省略--

  <%= stylesheet_link_tag 'application', media: 'all' %>
  <%= javascript_include_tag 'application' %>
  <%= javascript_pack_tag 'application' %>

--省略--

</head>
```
__'data-turbolinks-track': 'reload'__の部分を削除します。

# 実際にやってみた！

では、実践に移ります。
先ずは、下記の様に
1. 無限スクロール
2. 右下上へボタン
が、application.jsで実装されてたとします。

```js:app/assets/javascripts/application.js
// 無限スクロール
$(function() {
  $('.jscroll').jscroll({
    contentSelector: '.skill-list',
    nextSelector: 'span.next:last a',
    loadingHtml: '<i class="fas fa-sync-alt fa-lg fa-spin"></i>'
  });
});


// 右下上へボタン
$(function() {
  var appear = false;
  var pagetop = $('#page_top');
  $(window).scroll(function () {
    if ($(this).scrollTop() > 100) {  //100pxスクロールしたら
      if (appear == false) {
        appear = true;
        pagetop.stop().animate({
          'right': '5px' //右から5pxの位置に
        }, 300); //0.3秒かけて現れる
      }
    } else {
      if (appear) {
        appear = false;
        pagetop.stop().animate({
          'right': '-50px' //右から-50pxの位置に
        }, 300); //0.3秒かけて隠れる
      }
    }
  });
  pagetop.click(function () {
    $('body, html').animate({ scrollTop: 0 }, 500); //0.5秒かけてトップへ戻る
    return false;
  });
});
```
↓下記の様に. __1. 無限スクロール__と__2. 右下上へボタン__読み込み中には、下記のような表示になります。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688029/c7acd6be-a53a-6750-0ca5-0c3961d755b0.png)



そして、下記サイトの左上のアイコン画像をクリックする事で画面遷移して、投稿一覧を表示する仕様です。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688029/ba919aaa-785d-4371-6ad4-a802a79c91c9.png)


左上のアイコン画像のViewは、↓この様になっています。

```:app/views/layouts/application.html.erb
<%= link_to posts_path, data: {"turbolinks"=>false} do %>
　<%= image_tag("/illusttube.png", class:"title_image") %>
<% end %>
```
この場合は、__2.view(.html.erb)のリンク毎に変更する。__を採用して、__data: {"turbolinks"=>false}__を利用しているので、__何回ページ遷移を実施してもその都度、ページ全体(HTML, CSS, JavaScript)を再読み込み__します。

![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688029/955226e1-84aa-d381-b2b8-0f6900a61773.gif)

## Viewのターボリンクを消してみる
```:app/views/layouts/application.html.erb
<%= link_to posts_path do %>
　<%= image_tag("/illusttube.png", class:"title_image") %>
<% end %>
```

この場合は、１度目の読み込みでは、__1. 無限スクロール__と__2. 右下上へボタン__が表示されますが、2度目の読み込みでは、
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688029/2d348ee3-ed9b-afdc-60aa-799ff7161036.png)
上記のように__1. 無限スクロール__と__2. 右下上へボタン__が表示されません。

...という事は、イコール__JavaScriptが読み込まれていない。__ということになります。

## jQueryの記述を変更
↓下記の様に__$(document).on('turbolinks:load', を追加__します。

```js:app/assets/javascripts/application.js
// 無限スクロール $(document).on('turbolinks:load', 追加
$(document).on('turbolinks:load', function() {
  $('.jscroll').jscroll({
    contentSelector: '.skill-list',
    nextSelector: 'span.next:last a',
    loadingHtml: '<i class="fas fa-sync-alt fa-lg fa-spin"></i>'
  });
});



// 右下上へボタン $(document).on('turbolinks:load', 追加
$(document).on('turbolinks:load', function() {
  var appear = false;
  var pagetop = $('#page_top');
  $(window).scroll(function () {
    if ($(this).scrollTop() > 100) {  //100pxスクロールしたら
      if (appear == false) {
        appear = true;
        pagetop.stop().animate({
          'right': '5px' //右から5pxの位置に
        }, 300); //0.3秒かけて現れる
      }
    } else {
      if (appear) {
        appear = false;
        pagetop.stop().animate({
          'right': '-50px' //右から-50pxの位置に
        }, 300); //0.3秒かけて隠れる
      }F
    }
  });
  pagetop.click(function () {
    $('body, html').animate({ scrollTop: 0 }, 500); //0.5秒かけてトップへ戻る
    return false;
  });
});
```F
↓__, data: {"turbolinks"=>false}__の部分を削除します。

```:app/views/layouts/application.html.erb
<%= link_to posts_path do %>
　<%= image_tag("/illusttube.png", class:"title_image") %>
<% end %>
```
↓html.erbのリンクが無い状態でも__2.view(.html.erb)のリンク毎に変更する。__と同じように何度リンク遷移してもJavaScriptを読み込むことができます。
![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688029/955226e1-84aa-d381-b2b8-0f6900a61773.gif)

再度になりますが、個人的には、__1.JavaScriptの記述を変更する。__を一番おすすめします！
以上です。長文の記事をここまで読んでくださった皆様に感謝致します。

では、また！