5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ruby on RailsAdvent Calendar 2016

Day 2

Turbolinks 5とJavaScriptウィジェットを共存させる

Posted at

Turbolinks 5にすると、JavaScriptまわりで正しく動かすためにいろいろ工夫が必要となりました。今回は、MagicSuggestというウィジェットを例に、対応するためにしたことをまとめていきます。

なお、自分が以前書いた、Turbolinks 5に関する記事もご覧いただくと、よりわかりやすいかもしれません。

Turbolinks 5の場合に、注意すべき点

「ページ表示のタイミングでのイベントが違う」という点は以前にも触れましたが、それ以外にも、「ブラウザバックした場合」の挙動がふつうの場合と大きく違っています。以下に、ブラウザバックする場合の概ねの流れを示してみます。

  1. ページを表示する(turbolinks:loadなどのイベントが流れる)
  2. ページ遷移しようとする(Turbolinksでの処理が起きる)
  3. 現在のページでturbolinks:before-cacheイベントが走る
  4. 現在のページのDOMを保存する
  5. 移動した先のDOMを描画する
  6. (移動先のページ)
  7. ブラウザバックを押すと、Turbolinksに制御が移る
  8. 保存したDOMをロードして、turbolinks:loadイベントを実行する
  9. ロードしたDOMを描画して、戻る処理が完了

ここで注意点としては、DOMの保存は.cloneNode(true)で行われるので、ネイティブ・jQueryを問わず、イベントはすべて削除されます。とはいえ、一般的な場合、

  • DOMノード…保存して復活するので、問題なく動く
  • JavaScriptイベント…turbolinks:loadでセットすれば、新規表示時にも発動し、ブラウザバック時にも全削除→再セットとなって、問題なし

となるので、正常に動く…ように思えます。

JavaScriptからウィジェットを追加する場合

ここで問題になるのが、JavaScriptからDOMまで変えてしまうようなウィジェットを追加するような場合です。そのままにしていると、

  • 新規ロード時…DOMを書き換えてウィジェットを生成、それにイベントをセット
  • ブラウザバック時…「書き換え後のDOM+イベントなし」の状態からウィジェット生成コードが走って、うまく動かない場合がある

作戦としては2つあって、自分でDOM変更やイベントのセットを書いている場合には、「すでにDOMが変更されていればバイパスする」という方法があります。ただ、プラグインで行う場合はそうも行きません。

MagicSuggestとは

今回テーマとして取り上げたMagicSuggestは、複数選択可能なselectをドロップダウン形式にしてしまう、というもので、設定次第ではQiitaのタグ欄のような「フリー入力+既存リストから選択」ということもできます。

そこそこ長さのあるリストから複数選択させたいときに便利だったのでよく使っていたのですが、Turbolinks 5になったことで対応が必要となりました。

動くようにはなりました

「MagicSuggestのイベントだけセット」という器用な芸当はできないので、「もともとあった<select>要素をバックアップしておいて、キャッシュ記録時に書き戻す」という方法で対応を行うことにしました。なお、jQuery側に手当をして、ページ遷移ごとにreadyが発動するようにしてあります。

app/assets/javascript/magic_suggest.js
//= require magicsuggest

jQuery(function() {
  'use strict';
  // ページバック時の書き戻し用に、本来のドロップダウンをバックアップ
  var params = {
    /* 略 */
  };
  $('.magic-suggest').each(function(){
    var $elem = $(this);
    var cloned = this.cloneNode(true);
    $elem.magicSuggest(params);
    $('#' + this.id).data('original_select', cloned);
  });
  $(document).one('turbolinks:before-cache', function(){
    $('.ms-ctn').each(function(){
      var $this = $(this);
      $this.replaceWith($this.data('original_select'));
    });
  });
});

バックアップからの書き戻しは一度でいいので、.oneで単発イベントとしてあります。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?