Rails 4: como usar $ (document) .ready () con turbo-links

422

Me encontré con un problema en mi aplicación Rails 4 al intentar organizar los archivos JS "a la manera de los rieles". Anteriormente estaban dispersos en diferentes puntos de vista. Los organicé en archivos separados y los compilé con la canalización de activos. Sin embargo, acabo de enterarme de que el evento "listo" de jQuery no se activa en los clics posteriores cuando se activa el enlace turbo. La primera vez que carga una página, funciona. Pero cuando hace clic en un enlace, todo lo que está dentro ready( function($) {no se ejecutará (porque la página no se vuelve a cargar). Buena explicación: aquí .

Entonces, mi pregunta es: ¿Cuál es la forma correcta de garantizar que los eventos jQuery funcionen correctamente mientras están activados los enlaces turbo? ¿Envuelve los scripts en un oyente específico de Rails? ¿O tal vez los rieles tienen algo de magia que lo hace innecesario? Los documentos son un poco vagos sobre cómo debería funcionar esto, especialmente con respecto a la carga de múltiples archivos a través de los manifiestos como application.js.

emersonthis
fuente

Respuestas:

554

Esto es lo que hago ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

la última línea escucha la carga de la página, que es lo que activarán los enlaces turbo.

Editar ... agregando la versión de Javascript (por solicitud):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Edit 2 ... For Rails 5 (Turbolinks 5) se page:loadconvierte turbolinks:loade incluso se disparará en la carga inicial. Entonces podemos hacer lo siguiente:

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

  ...your javascript goes here...

});
Meltemi
fuente
3
¿Es ese guión de café? Aún no lo he aprendido. ¿Hay un js o jQuery equivalente a su ejemplo?
Emersonthis
66
Creo que lo tengo: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)¿Hay alguna preocupación por ambos .ready y por 'page:load'ser activado?
Emersonthis
44
@Emerson hay un sitio web muy útil, con un nombre explícito: js2coffee.org
MrYoshiji
16
Puedo confirmar que la función de listo no se llama dos veces. Si se trata de una actualización completa, soloready se activa el evento. Si se trata de una carga de turbolinks, solopage:load se dispara el evento. Es imposible para ambos readyy page:loadser despedidos en la misma página.
Christian
77
FYI También puede aplicar esto para múltiples eventos page:loade readyincluir una cadena separada por espacios para el primer argumento. $(document).on('page:load ready', ready);El segundo argumento es simplemente una función, que se nombra ready, siguiendo el ejemplo de esta respuesta.
ahnbizcad
235

Acabo de enterarme de otra opción para resolver este problema. Si carga la jquery-turbolinksgema , vinculará los eventos de Rails Turbolinks a los document.readyeventos para que pueda escribir su jQuery de la manera habitual. Simplemente agrega jquery.turbolinksjusto después jqueryen el archivo de manifiesto js (por defecto:) application.js.

emersonthis
fuente
2
¡Genial gracias! Exactamente lo que necesito. El camino de los rieles. Convención.
Jeremy Becker
66
De acuerdo con la documentación , debe agregar //= require jquery.turbolinksal manifiesto (por ejemplo, application.js).
apocryphalauthor
1
@wildmonkey las dos respuestas son mutuamente excluyentes. 0.o
ahnbizcad
1
Debe reiniciar su servidor de rails cuando agregue esta gema
Toby 1 Kenobi
21
Tenga en cuenta que Rails 4 ahora está predeterminado en Turbolinks 5 que, a su vez, no es compatible con jquery.turbolinks! Ver github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian
201

Recientemente encontré la forma más limpia y fácil de entender:

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

O

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

EDITAR
Si ha delegado eventos vinculados al document, asegúrese de adjuntarlos fuera de la readyfunción, de lo contrario, se recuperarán en cada page:loadevento (haciendo que las mismas funciones se ejecuten varias veces). Por ejemplo, si tiene alguna llamada como esta:

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

Sácalos de la readyfunción, así:

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

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

Los eventos delegados vinculados al documentno necesitan vincularse en el readyevento.

Artyom Tsoy
fuente
99
Esto es mucho más simple que la respuesta aceptada de crear una ready()función separada . Votos a favor adicionales para la explicación de eventos delegados vinculantes fuera de la readyfunción.
Christian
1
En desventaja de este método es que $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jQuery / listas
mfazekas
@Dezi @mfazekas ¿Funcionaría esta solución si está utilizando versiones anteriores de jQuery y SIN la gema jquery-turbolinks? ¿O la readyporción se invalida al usar la gema?
ahnbizcad
La forma más sencilla y fácil de hacerlo. Es compatible con el código js distribuido en varios archivos y no tengo que molestarme en el orden en que se incluyeron. Mucho más limpio que la asignación variable.
Evgenia Manolova
3
Probablemente debería usar "page: change" en lugar de "page: load", ya que el primero se activa si la página se carga desde el servidor o desde el caché del lado del cliente .
Nathan Long
91

Encontré esto en la documentación de Rails 4 , similar a la solución de DemoZluk pero un poco más corta:

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

O

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

Si tiene scripts externos que llaman $(document).ready()o si no puede molestarse en reescribir todo su JavaScript existente, entonces esta gema le permite seguir usando $(document).ready()TurboLinks: https://github.com/kossnocorp/jquery.turbolinks

migu
fuente
79

Según las nuevas guías de rieles , la forma correcta es hacer lo siguiente:

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

o, en coffeescript:

$(document).on "turbolinks:load", ->
alert "page has loaded!"

No escuches el evento $(document).readyy solo se disparará un evento. Sin sorpresas, no es necesario usar la gema jquery.turbolinks .

Esto funciona con los rieles 4.2 y superiores, no solo con los rieles 5.

vedant
fuente
bueno, esto funcionó para mí. Intenté la solución aquí: stackoverflow.com/questions/17881384/… pero no funcionó por alguna razón. Ahora cargo en turbolinks: cargar en su lugar
krinker
1
¿Alguien puede confirmar que el "turbolinks:load"evento funciona en Rails 4.2? El turbolinks:prefijo del evento se introdujo en los Nuevos Turbolinks y no se usa en Rails 4.2 IIRC que usa Turbolinks Classic . Considera probar "page:change"si "turbolinks:load"no funciona en tu aplicación anterior a Rails 5. Ver guías.rubyonrails.org
Eliot Sykes
1
@EliotSykes La guía no se ha actualizado. Rails 4.2 ahora usa github.com/turbolinks/turbolinks en lugar de turbolinks-classic. Y en cualquier caso, esto depende de lo que haya especificado en su Gemfile. Espero que confirmes lo mismo pronto :)
vedant
3
Gracias @ vedant1811. Al momento de escribir, estoy confirmando que una nueva aplicación Rails 4.2.7 usa Turbolinks 5 (no turbolinks-classic). Desarrolladores que lean esto: compruebe su Gemfile.lock para ver la versión de turbolinks utilizada. Si es inferior a 5.0, utilice page:changeo actualice los enlaces turbo. También encontré esto, puede ser relevante para algunos, donde turbolinks traduce eventos a los nombres de eventos antiguos: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Eliot Sykes
1
¿Es alert "page has loaded!"necesario sangrar para que la función de devolución de llamada la ejecute realmente?
mbigras
10

NOTA: Consulte la respuesta de @ SDP para obtener una solución limpia e integrada

Lo arreglé de la siguiente manera:

asegúrese de incluir application.js antes de que se incluyan los otros archivos js dependientes de la aplicación cambiando el orden de inclusión de la siguiente manera:

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

Defina una función global que maneje el enlace en application.js

// application.js
window.onLoad = function(callback) {
  // binds ready event and turbolink page:load event
  $(document).ready(callback);
  $(document).on('page:load',callback);
};

Ahora puedes unir cosas como:

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

// equivalent in javascript:
onLoad(function() {
  $('a.clickable').click(function() {
    alert('link clicked');
});
trineo
fuente
2
Esto parece lo más limpio, ya que solo tiene que agregar esto en un solo lugar, no en todos los archivos coffeescript. ¡Buen trabajo!
pyrospade el
@sled Entonces, ¿esto dice usar el método de SDP para usar la gema y este código? ¿O es esta una respuesta que no usa la gema?
ahnbizcad
olvide esta respuesta y use la solución de SDP.
trineo
@sled ¿Es posible combinar la solución de SDP con la idea de poner la llamada solo en un lugar?
ahnbizcad
1
@gwho: muestra la respuesta de Como SDP, turbolinks conecta al readyevento, esto significa que puede utilizar la forma "estándar" de inicialización: $(document).ready(function() { /* do your init here */});. Se debe llamar a su código de inicio cuando se carga la página completa (-> sin turbolinks) y su código de inicio se debe ejecutar nuevamente cuando carga una página a través de turbolinks. Si desea ejecutar algún código sólo cuando se ha utilizado un Turbolink:$(document).on('page:load', function() { /* init only for turbolinks */});
trineo
8

Nada de lo anterior funciona para mí, resolví esto al no usar jQuery's $ (document) .ready, sino que utilicé addEventListener en su lugar.

document.addEventListener("turbolinks:load", function() {
  // do something
});
RockL
fuente
7
$(document).on 'ready turbolinks:load', ->
  console.log '(document).turbolinks:load'
Sukeerthi Adiga
fuente
Esa es la única solución que funciona para mí en todos y cada uno de los navegadores, para Turbolinks> = 5 y que se activa tanto en la carga de la primera página como también cuando haces clic en cualquier enlace (con turbolinks). Ese es el caso porque mientras desencadenantes Chrome turbolinks:loaden la primera carga de la página, Safari no y la forma en hacky para arreglarlo (activación turbolinks:loadde application.js) obviamente rompe Chrome (que dispara dos veces con esto). Esta es la respuesta correcta. Definitivamente ir con los 2 eventos readyy turbolinks:load.
lucasarruda
5

Tienes que usar:

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

de turbolinks doc .

Gian Franco Fioriello
fuente
2

En lugar de utilizar una variable para guardar la función "lista" y vincularla a los eventos, es posible que desee activar el readyevento siempre que se active page:load.

$(document).on('page:load', function() {
  $(document).trigger('ready');
});
Wachunei
fuente
2

Esto es lo que he hecho para garantizar que las cosas no se ejecuten dos veces:

$(document).on("page:change", function() {
     // ... init things, just do not bind events ...
     $(document).off("page:change");
});

Creo que usar la jquery-turbolinksgema o combinar $(document).readyy / $(document).on("page:load")o usar $(document).on("page:change")solo se comporta inesperadamente, especialmente si estás en desarrollo.

Kemmey gris
fuente
¿Te importa explicar a qué te refieres con comportamientos inesperados?
sunnyrjuneja
Si solo tengo una alerta simple sin el $(document).offbit en esa función anónima de "cambio de página", obviamente alertará cuando llegue a esa página. Pero al menos en el desarrollo que ejecuta WEBrick, también alertará cuando haga clic en un enlace a otra página. En el peor de los casos, alerta dos veces cuando hago clic en un enlace a la página original.
Gray Kemmey
2

Encontré el siguiente artículo que funcionó muy bien para mí y detalla el uso de lo siguiente:

var load_this_javascript = function() { 
  // do some things 
}
$(document).ready(load_this_javascript)
$(window).bind('page:change', load_this_javascript)
atw
fuente
2
$(document).ready(ready)  

$(document).on('turbolinks:load', ready)
Konstantinos Mou
fuente
2
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
andreas
44
En realidad, esta respuesta es incorrecta. El turbolinks:loadevento se dispara para la carga de la página inicial, así como después de seguir los enlaces. Si adjunta un evento tanto al readyevento estándar como al turbolinks:loadevento, se disparará dos veces la primera vez que visite una página.
alexanderbird
Esta respuesta es incorrecta. Como dijo @alexanderbird, hace readyque se dispare dos veces en las siguientes cargas de página. Por favor cámbielo o elimínelo.
Chester
@alexanderbird Tu comentario resolvió uno de los problemas para mí
Anwar,
1

Probado tanta solución finalmente llegó a esto. Este código definitivamente no se llama dos veces.

      var has_loaded=false;
      var ready = function() {
        if(!has_loaded){
          has_loaded=true;
           
          // YOURJS here
        }
      }

      $(document).ready(ready);
      $(document).bind('page:change', ready);

Simon Meyborg
fuente
1

Normalmente hago lo siguiente para mis proyectos de rails 4:

En application.js

function onInit(callback){
    $(document).ready(callback);
    $(document).on('page:load', callback);
}

Luego, en el resto de los archivos .js, en lugar de usar $(function (){}), llamoonInit(function(){})

muZk
fuente
0

Primero, instale la jquery-turbolinksgema. Y luego, no se olvide de mover los archivos Javascript comprendidos desde el extremo del cuerpo de su application.html.erba su <head>.

Como se describe aquí , si ha puesto el enlace javascript de la aplicación en el pie de página por razones de optimización de velocidad, deberá moverlo a la etiqueta para que se cargue antes que el contenido de la etiqueta. Esta solución funcionó para mí.

Aboozar Rajabi
fuente
0

Me encontré con mis funciones se duplicaron cuando se utiliza una función para readyy turbolinks:loadpor lo que utiliza,

var ready = function() {
  // you code goes here
}

if (Turbolinks.supported == false) {
  $(document).on('ready', ready);
};
if (Turbolinks.supported == true) {
  $(document).on('turbolinks:load', ready);
};

De esa manera, sus funciones no se duplican si se admiten turbolinks

Lee Eather
fuente