Rails 4:如何在turbo-links中使用$(document).ready()?

430

在尝试使用"Rails方式"组织JS文件时,我在我的Rails 4应用程序中遇到了问题。这些文件以前分散在不同的视图中。我将它们整理成单独的文件,并使用资源管道进行编译。但是,我刚刚学到当开启Turbo-links时,jQuery的 "ready"事件不会在后续的点击中触发。第一次加载页面时它可以正常工作。但是当您单击链接时,任何在 ready( function($) {内部的内容都不会被执行(因为实际上页面并没有重新加载)。详细解释请参考:这里

所以我的问题是: 在开启Turbo-links的同时如何确保jQuery事件能够正常工作?您是否需要将脚本包装在Rails特定的侦听器中?或者说Rails有某种神奇的功能使其不必要?文档对于如何使用manifest(s)加载多个文件的方式有些模糊。

20个回答

561

这是我的做法... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

最后一行监听页面加载,这正是Turbo Links将触发的内容。

编辑...添加JavaScript版本(按要求):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

编辑2...对于Rails 5(Turbolinks 5),page:load变为turbolinks:load,并且会在初始加载时触发。因此我们只需要执行以下操作:

$(document).on('turbolinks:load', function() {

  ...your javascript goes here...

});

3
那是咖啡语言吗?我还没学过。你的例子有没有对应的js或jQuery版本? - emersonthis
6
我知道了:var ready = function() { ... } $(document).ready(ready) 表示在文档加载完成后运行 ready 函数,而 $(document).on('page:load', ready) 表示在页面重新加载时运行相同的函数。你的问题是是否需要考虑同时触发 .ready'page:load' 的情况。 - emersonthis
4
@Emerson,有一个非常有用的网站,名字很明确:http://js2coffee.org。 - MrYoshiji
16
我可以确认 ready 函数不会被调用两次。如果是强制刷新,只会触发 ready 事件。如果是 turbolinks 加载,只会触发 page:load 事件。在同一页中同时触发 readypage:load 是不可能的。 - Christian
8
FYI,您还可以将此应用于多个事件“page:load”和“ready”,只需在第一个参数中包含以空格分隔的字符串即可。例如,$(document).on('page:load ready', ready); 第二个参数是一个简单的函数,该函数恰好被命名为“ready”,参考此答案的示例。 - ahnbizcad
显示剩余14条评论

235
我刚刚了解到另一种解决这个问题的选择。如果您加载jquery-turbolinks gem,它将把Rails Turbolinks事件绑定到document.ready事件上,这样您就可以按照通常的方式编写jQuery。您只需在js清单文件中(默认为:application.jsjquery之后添加jquery.turbolinks即可。

2
太棒了,谢谢!正是我想要的。 Rails 的方法。约定俗成。 - Jeremy Becker
6
根据文档,您应该在清单文件(例如application.js)中添加//= require jquery.turbolinks。请注意,此操作旨在使jquery.turbolinks工作。 - kindrobot
1
当你添加这个 gem 后,你需要重新启动你的 Rails 服务器。 - Toby 1 Kenobi
1
我仍然无法使用这个方法让Turbolinks与Select2正常配合。 - Batman
21
请注意,Rails 4 现在默认使用 Turbolinks 5,而这个版本不支持 jquery.turbolinks!请参阅 https://github.com/kossnocorp/jquery.turbolinks/issues/56。 - sebastian
显示剩余9条评论

200

最近我发现了一种最清晰易懂的处理方式:

$(document).on 'ready page:load', ->
  # Actions to do

或者

$(document).on('ready page:load', function () {
  // Actions to do
});

编辑
如果您已将委托事件绑定到document,请确保在ready函数之外附加它们,否则它们将在每个page:load事件上重新绑定(导致相同的函数运行多次)。例如,如果您有任何这样的调用:

$(document).on 'ready page:load', ->
  ...
  $(document).on 'click', '.button', ->
    ...
  ...

把它们从ready函数中移出来,像这样:

$(document).on 'ready page:load', ->
  ...
  ...

$(document).on 'click', '.button', ->
  ...

委托事件绑定到 document 上时不需要在 ready 事件上绑定。


9
这比创建一个单独的ready()函数要简单得多。额外的赞赏是为了解释如何在ready函数之外绑定委托事件。 - Christian
1
这种方法的一个缺点是 $(document).on( "ready", handler ),自 jQuery 1.8 起已被弃用。jquery/ready - mfazekas
@Dezi @mfazekas 如果您使用早期版本的jQuery且没有使用jquery-turbolinks gem,这个解决方案是否可行?或者是通过使用gem使ready部分无效? - ahnbizcad
最简单和最容易的方法。它支持在多个文件中分布的js代码,我不必担心它们被包含的顺序。比变量赋值更清晰。 - Evgeniya Manolova
3
你可能应该使用"page:change"而不是"page:load",因为前者会在页面从服务器加载或从客户端缓存加载时触发。参考链接:https://github.com/rails/turbolinks/#events。 - Nathan Long

90

Rails 4文档中发现了这个方法,与DemoZluk的解决方案类似但稍微更短:

$(document).on 'page:change', ->
  # Actions to do

或者

$(document).on('page:change', function () {
  // Actions to do
});

如果你有调用$(document).ready()的外部脚本,或者你不想重写所有现有的JavaScript代码,那么这个宝石可以让你在TurboLinks中继续使用$(document).ready()https://github.com/kossnocorp/jquery.turbolinks


81

根据新的Rails指南,正确的方法是执行以下操作:

$(document).on('turbolinks:load', function() {
   console.log('(document).turbolinks:load')
});

或者,用 CoffeeScript 写成:

$(document).on "turbolinks:load", ->
alert "page has loaded!"
不要监听事件$(document).ready,只会触发一个事件。没有惊喜,不需要使用jquery.turbolinks gem。
这适用于Rails 4.2及以上版本,不仅适用于Rails 5。

1
有人能确认在Rails 4.2中"turbolinks:load"事件是否有效吗?turbolinks:事件前缀是在新的Turbolinks中引入的,在Rails 4.2中没有使用,如果"turbolinks:load"在您的Rails 5之前的应用程序中无法正常工作,请尝试"page:change"。请参阅http://guides.rubyonrails.org/v4.2.7/working_with_javascript_in_rails.html#page-change-events - Eliot Sykes
1
@EliotSykes 这个指南还没有更新。Rails 4.2 现在使用 https://github.com/turbolinks/turbolinks 而不是 turbolinks-classic。无论如何,这取决于您在 Gemfile 中指定的内容。 希望您能尽快确认 :) - Vedant Agarwala
3
谢谢@vedant1811。在撰写本文时,我确认一个新的Rails 4.2.7应用程序使用Turbolinks 5(而不是turbolinks-classic)。开发人员阅读此内容 - 请检查您的Gemfile.lock中所使用的turbolinks版本。如果版本低于5.0,请使用“page:change”或升级turbolinks。还发现了这个问题,可能对一些人有用,其中turbolinks将事件转换为旧的事件名称:https://github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/compatibility.coffee - Eliot Sykes
1
“alert”语句“页面已加载!”需要缩进才能由回调函数实际运行吗? - mbigras
@EliotSykes 我正在使用Rails 4.2.8,它可以正常工作!谢谢 - Anwar
显示剩余4条评论

10

注意:有一个干净的内置解决方案,请参见@SDP的答案

我已经修复了它,方法如下:

请确保在其他与应用程序相关的js文件被包含之前,先包含application.js,通过更改包含顺序实现:

// in application.js - make sure `require_self` comes before `require_tree .`
//= require_self
//= require_tree .

定义一个全局函数,在`application.js`中处理绑定。
// application.js
window.onLoad = function(callback) {
  // binds ready event and turbolink page:load event
  $(document).ready(callback);
  $(document).on('page:load',callback);
};

现在,您可以绑定类似以下内容的东西:

// in coffee script:
onLoad ->
  $('a.clickable').click => 
    alert('link clicked!');

// equivalent in javascript:
onLoad(function() {
  $('a.clickable').click(function() {
    alert('link clicked');
});

2
这似乎是最干净的,因为您只需要在一个地方添加它,而不是每个CoffeeScript文件。 做得好! - pyrospade
@sled 这是在说要使用SDP的方法来使用gem和这段代码吗?还是这个答案没有使用gem呢? - ahnbizcad
请忘记这个答案并使用SDP的解决方案。 - sled
@sled 是否可能将SDP的解决方案与仅在一个地方放置呼叫的概念相结合? - ahnbizcad
1
如SDP的回答所示,turbolinks连接到“ready”事件,这意味着您可以使用“标准”的初始化方式:$(document).ready(function() { /* do your init here */});。您的初始化代码应在完整页面加载时调用(->没有turbolinks),并且当您通过turbolinks加载页面时,应再次执行您的初始化代码。如果您想仅在使用turbolink时执行某些代码:$(document).on('page:load', function() { /* init only for turbolinks */}); - sled
@sled 我明白了。init code 就是你想要执行的代码。所以我猜 $(document).on('ready page:load ', function(){} ); 对于 ready 不起作用,正如在 gem 的 github readme 中所记录的那样。 - ahnbizcad

9

以上方法都没对我起作用,我通过不使用jQuery的$(document).ready方法,改为使用addEventListener方法来解决这个问题。

document.addEventListener("turbolinks:load", function() {
  // do something
});

8
$(document).on 'ready turbolinks:load', ->
  console.log '(document).turbolinks:load'

这是我在每个浏览器上都能使用的唯一解决方案,适用于Turbolinks> = 5,并且在第一次页面加载时以及单击任何链接时都会触发(使用turbolinks)。这是因为虽然Chrome在第一次页面加载时会触发“ turbolinks:load”,但Safari不会,而修复它的hacky方法(在“ application.js”上触发“ turbolinks:load”)显然会破坏Chrome(使用此方法会触发两次)。这是正确的答案。 一定要使用2个事件“ready”和“turbolinks:load”。 - lucasarruda

6

你需要使用:

document.addEventListener("turbolinks:load", function() {
  // your code here
})

from turbolinks doc.


3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接