質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • Ruby on Rails

    7471questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

  • jQuery

    6915questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • Ruby on Rails 4

    2455questions

    Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

【Rails/jQuery】turbolinks をオフにしないままで、.hover を発火させたり.offset().top を取得したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 2,356

jusco

score 72

RailsでTubolinksをオンにしてjQueryを使用する場合、通常通りの記述ではreadyが動かないということがわかったため、その対策を記入しました。
それによってTubolinksをオフにせずともいくつかのjsファイルはうまく動作したのですが、以下の2機能についてはreadyがうまく発火していないようで、ページを再読み込みしないと動作しません。

今回実装したい機能は以下の2点です。
1.「#fixMenu」の固定
2.「.MenuBody」の表示切り替え

まず1.について。
var ready;
ready = function() {

$(document).ready(function() {
  var nav    = $('#fixMenu'),
      offset = nav.offset();

  $(window).scroll(function () {
    if($(window).scrollTop() > offset.top - 20) {
      nav.addClass('fixed');
    } else {
      nav.removeClass('fixed');
    }
  });
});

 };
$(document).ready(ready);
$(document).on('page:load', ready);

上記の記述でreadyの対策はできているかと思うのですが、ページ遷移後にはうまく動作せず、「Uncaught TypeError: Cannot read property 'top' of undefined」とエラーが出てしまいます。


また、2について、
var ready;
ready = function() {
 $(function() {
    $('.toggleMenu').click(function() {
      if ($('.MenuBody').css('display') == 'block') {
        $('.MenuBody').fadeOut('800');
      }else{
        $('.MenuBody').slideDown('1000','swing');

      }
    });
    $('.toggleMenu,.MenuBody').hover(function(){
      over_flag = true;
    }, function(){
      over_flag = false;
    });
    $('body').click(function() {
      if (over_flag == false) {
        $('.MenuBody').fadeOut('800');

      }
    });
  });
};
$(document).ready(ready);
$(document).on('page:load', ready);

こちらも同様に、ページ遷移後はうまく動作せず、再読み込みすればうまく動作します。
ページ遷移後は、「.MenuBody」は上から降りてはくるのですが、降りてくると同時にフェードアウトして消えてしまいます。
おそらく「.hover」の発火がうまくいってないのではないかと思いますが、「Uncaught ReferenceError: over_flag is not defined」というエラーが出ています。


jquery.turbolinksを使用しても同様の結果でした。
どのようにすればこれらのjQueryを正常動作させることができるでしょうか。


ご回答よろしくお願いいたします。


1. について、hello-world様から頂いた以下の回答で動作しました。
(function(){
  var  offset;

  $(window).scroll(function () {
    var nav    = $('#fixMenu');
    if(offset && $(window).scrollTop() > offset.top - 20) {
      nav.addClass('fixed');
    } else {
      nav.removeClass('fixed');
    }
  });

  $(document).on('ready page:load', function(){
    offset = $('#fixMenu').offset();
  })
})();
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

var ready;
ready = function() {
  var nav    = $('#fixMenu'),
      offset = nav.offset();

  $(window).scroll(function () {
    if($(window).scrollTop() > offset.top - 20) {
      nav.addClass('fixed');
    } else {
      nav.removeClass('fixed');
    }
  });
};
$(document).ready(ready);
$(document).on('page:load', ready);

です。readyon(page:load)に渡す関数の中でさらにreadyに渡してもうまくいきません


改善案

$(window).scroll(function () {
  var nav    = $('#fixMenu'),
      offset = nav.offset();
  if($(window).scrollTop() > offset.top - 20) {
    nav.addClass('fixed');
  } else {
    nav.removeClass('fixed');
  }
});


案2

(function(){
  var offset;

  $(window).scroll(function () {
    if(offset && $(window).scrollTop() > offset.top - 20) {
      nav.addClass('fixed');
    } else {
      nav.removeClass('fixed');
    }
  });

  $(document).on('ready page:load', function(){
    offset = $('#fixMenu').offset();
  })
})();


2.「.MenuBody」の表示切り替え 

1. イベントの登録がおかしかったのを修正しました。$(document).on('ready page:load', function(){
2. アニメーションの途中で消えるということだったので、アニメーションが完了してからcanFadeクラスを追加するようにした
3. over_flag変数が正しく宣言されていなかったので、代わりにisHoverクラスとして要素に持たせた

$(document).on('ready page:load', function(){
  $('.toggleMenu').click(function() {
    if ($('.MenuBody').css('display') != 'block') {
      $('.MenuBody').slideDown('1000','swing', function() {
        $('.MenuBody').addClass('canFade');
      });
    } else if($('.MenuBody').hasClass('canFade')) {
      $('.MenuBody').fadeOut('800');
    }
  });
  $('.toggleMenu,.MenuBody').hover(function(){
    $('.MenuBody').addClass('isHover');
  }, function(){
    $('.MenuBody').removeClass('isHover');
  });
  $('body').click(function() {
    if ($('.MenuBody').hasClass('isHover') == false && $('.MenuBody').hasClass('canFade')) {
      $('.MenuBody').fadeOut('800');
    }
  });
});


投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/08 14:56

    ご回答有難うございます!
    readyを減らして、教えていただいたとおりに記述してみましたがうまく動作しませんでした(T_T)

    キャンセル

  • 2015/08/08 22:30

    > 上記の記述でreadyの対策はできているかと思うのですが、ページ遷移後にはうまく動作せず、「Uncaught TypeError: Cannot read property 'top' of undefined」とエラーが出てしまいます。

    まだこのエラーがでているのなら、offset が undefinedだというエラーなので

    var nav = $('#fixMenu'),
    offset = nav.offset();

    のすぐ下で
    console.log(nav, offset);
    などして、値を確認するのはどうでしょうか。

    キャンセル

  • 2015/08/10 10:03

    ご回答有難うございます。

    console.log(nav, offset);

    を記入したところ、

    [div#fixMenu, context: document, selector: "#fixMenu"] Object {top: 270, left: 842}

    このように値が取得できてはいるようなのですが、ページ遷移後にリロードを行わない場合、

    Uncaught TypeError: Cannot read property 'top' of undefined(anonymous function)

    といったエラーメッセージが出てしまいます。

    ページをリロードすることでこのエラーは消え、通常通りにjqueryが作動します…。

    キャンセル

  • 2015/08/10 10:31

    追記しました。
    scrollイベントは、readyを待たずすぐに登録しても問題ないので、この方法でどうでしょう

    キャンセル

  • 2015/08/10 10:50

    ご回答ありがとうございます。

    早速試してみたところ、ページ遷移後でもすぐに動作辞退は確認できました!
    しかし、スクロールすると、固定部分が点滅したような表示になり、非常にカクカク動いているように見えてしまっています。
    時には消えてしまうこともあります・・・。

    キャンセル

  • 2015/08/10 11:16

    カタカタするということは、position: fixed がうまく動いていないということですよね?
    `fixed` クラスでは`position: fixed`が指定されているのでしょうか?


    何かのせいで、`fixed`クラスがついたり消えたりということはありませんか?
    if($(window).scrollTop() > offset.top - 20) {
      nav.addClass('fixed');
      console.log("add")
    } else {
      nav.removeClass('fixed');
      console.log("remove")
    }
    こんな感じのコードでログ中に removeが混じったりということはありませんか?

    キャンセル

  • 2015/08/10 12:08

    おっしゃる通りで、fixedクラスにはそのような指定をしています。
    確認したところ、スクロールすると、removeとaddがなんども繰り返しでログとして出ています。

    元々の記述では、画面遷移後は動作しないものの、リロードすればadd,removeが交互に動作するということはなく、うまく動作していました。
    しかし、リロードを待たずに登録する方法では、画面遷移後は動作するものの、add,removeが交互に動作してしまっています。

    キャンセル

  • 2015/08/10 13:07

    追記しました。これならどうでしょう

    キャンセル

  • 2015/08/10 13:48 編集

    (function(){
    var offset;

    $(window).scroll(function () {
    var nav = $('#fixMenu');
    if(offset && $(window).scrollTop() > offset.top - 20) {
    nav.addClass('fixed');
    } else {
    nav.removeClass('fixed');
    }
    });

    $(document).on('ready page:load', function(){
    offset = $('#fixMenu').offset();
    })
    })();


    ご回答いただいたものを元に、5行目に「var nav = $('#fixMenu');」を追記することでうまく動作しました!ありがとうございましたm(__)m

    もしよろしければ、2.についてもなにか分かりましたら教えて頂けると幸いです…m(__)m

    キャンセル

  • 2015/08/10 15:51

    質問2について追記しました。こんな感じでどうでしょうか。

    キャンセル

  • 2015/08/10 16:50

    ご回答いただいた内容で完全に動作の確認が取れました!!
    最後までご丁寧にお付き合い頂きまして、誠にありがとうございました…!!m(__)m

    キャンセル

+1

Turbolinksを有効にしていると、windowオブジェクトはページ遷移しても変化しませんので、ページ遷移のたびに$(window).scrollイベントをセットしていると、どんどん溜まってきてしまいます。前のページにあった#fixMenuを指したままのjQueryオブジェクトもクロージャ内に残って、うまく動作しません。

ということで、

1. 最初に一度だけ$(window).scrollへイベントをセットして、その中では$('body').trigger('window_scrolled');のようにカスタムイベントを中継するだけにする
2. readyとページ遷移の両方で駆動する関数の中で、$('body').on('window_scrolled')とカスタムイベントを拾う

という流れがいいのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/08 14:57

    ご回答有難うございます!
    指針を示して頂いてありがとうございます。

    流れはわかったのですが、実際にそれをどのように記述するのかがわかりません・・・。
    お手すきの際に、具体的なコードを教えて頂けないでしょうか。

    よろしくお願い致しますm(__)m

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.48%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Ruby on Rails

    7471questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

  • jQuery

    6915questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • Ruby on Rails 4

    2455questions

    Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。