Rails 4: come usare $ (document) .ready () con turbo-link


422

Ho riscontrato un problema nella mia app Rails 4 durante il tentativo di organizzare i file JS "alla guida". In precedenza erano sparsi in diverse viste. Li ho organizzati in file separati e li ho compilati con la pipeline delle risorse. Tuttavia, ho appena appreso che l'evento "pronto" di jQuery non si attiva ai clic successivi quando viene attivato il collegamento turbo. La prima volta che carichi una pagina funziona. Ma quando fai clic su un link, qualsiasi cosa all'interno di ready( function($) {esso non verrà eseguita (perché la pagina non si carica di nuovo). Buona spiegazione: qui .

Quindi la mia domanda è: qual è il modo giusto per garantire che gli eventi jQuery funzionino correttamente mentre i turbo-link sono attivi? Avvolgi gli script in un listener specifico per Rails? O forse le rotaie hanno della magia che la rende superflua? I documenti sono un po 'vaghi su come dovrebbe funzionare, soprattutto per quanto riguarda il caricamento di più file tramite manifest (s) come application.js.

Risposte:


554

Ecco cosa faccio ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

l'ultima riga ascolta il caricamento della pagina, che è ciò che i turbo link attiveranno.

Modifica ... aggiunta della versione Javascript (per richiesta):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Modifica 2 ... Per Rails 5 (Turbolink 5) page:loaddiventa turbolinks:loade verrà attivato anche al caricamento iniziale. Quindi possiamo semplicemente fare quanto segue:

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

  ...your javascript goes here...

});

3
È quella sceneggiatura del caffè? Non l'ho ancora imparato. Esiste un js o jQuery equivalente al tuo esempio?
emersonthis,

6
Pensi di averlo capito: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)c'è qualche preoccupazione per entrambi .ready e per 'page:load'essere innescato?
emersonthis,

4
@Emerson c'è un sito molto utile, con un nome esplicito: js2coffee.org
MrYoshiji

16
Posso confermare che la funzione ready non viene chiamata due volte. Se è un aggiornamento intenso, viene generato solo l' readyevento. Se si tratta di un carico di turbolink, viene generato solo l' page:loadevento. È impossibile per entrambi readye page:loadessere licenziato sulla stessa pagina.
Christian

7
FYI È inoltre possibile applicare questo per più eventi page:loade readyincludendo una stringa separata da spazio per il primo argumet. $(document).on('page:load ready', ready);Il secondo argomento è semplicemente una funzione, che sembra essere nominata ready, seguendo l'esempio di questa risposta.
ahnbizcad,

235

Ho appena saputo di un'altra opzione per risolvere questo problema. Se carichi la jquery-turbolinksgemma , gli eventi Rails Turbolink verranno associati agli document.readyeventi in modo da poter scrivere jQuery nel solito modo. Basta aggiungere jquery.turbolinkssubito dopo jquerynel file manifest js (per impostazione predefinita:) application.js.


2
Fantastico, grazie! Esattamente quello di cui ho bisogno. La via delle rotaie. Convenzione.
Jeremy Becker,

6
Secondo la documentazione , è necessario aggiungere //= require jquery.turbolinksal manifest (ad esempio application.js).
apocryphalauthor,

1
@wildmonkey le due risposte si escludono a vicenda. 0.o
ahnbizcad

1
Devi riavviare il tuo server rotaie quando aggiungi questa gemma
Toby 1 Kenobi,

21
Si noti che Rails 4 è ora impostato su Turbolink 5, che a sua volta non è supportato da jquery.turbolinks! Vedi github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian

201

Di recente ho trovato il modo più pulito e di facile comprensione per gestirlo:

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

O

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

MODIFICA
Se hai delegato eventi associati a document, assicurati di collegarli al di fuori della readyfunzione, altrimenti verranno rimbalzati su ogni page:loadevento (facendo sì che le stesse funzioni vengano eseguite più volte). Ad esempio, se hai chiamate come questa:

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

Portali fuori dalla readyfunzione, in questo modo:

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

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

Gli eventi delegati associati a documentnon devono essere associati readyall'evento.


9
Questo è molto più semplice della risposta accettata della creazione di una ready()funzione separata . Voti bonus per la spiegazione di eventi delegati vincolanti al di fuori della readyfunzione.
Christian

1
Svantaggio di questo metodo è che $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas

@Dezi @mfazekas Funzionerebbe questa soluzione se si utilizzano versioni precedenti di jQuery e SENZA la gemma jquery-turbolinks? O la readyporzione è resa non valida DA usando la gemma?
ahnbizcad,

Il modo più semplice e facile per farlo. Supporta il codice js distribuito in più file e non devo preoccuparmi dell'ordine in cui sono stati inclusi. Molto più pulito dell'assegnazione variabile.
Evgenia Manolova,


91

Lo abbiamo trovato nella documentazione di Rails 4 , simile alla soluzione di DemoZluk ma leggermente più breve:

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

O

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

Se hai script esterni che chiamano $(document).ready()o se non puoi preoccuparti di riscrivere tutto il tuo JavaScript esistente, allora questo gioiello ti consente di continuare a utilizzare $(document).ready()con TurboLinks: https://github.com/kossnocorp/jquery.turbolinks


79

Secondo le nuove guide di binari , il modo corretto è di effettuare le seguenti operazioni:

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

o, nel coffeescript:

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

Non ascoltare l'evento $(document).readye verrà generato solo un evento. Nessuna sorpresa, nessuna necessità di usare la gemma jquery.turbolinks .

Funziona con le guide 4.2 e successive, non solo con le guide 5.


bello questo ha funzionato per me. Ho provato la soluzione qui: stackoverflow.com/questions/17881384/… ma non ha funzionato per qualche motivo. Ora carico i turbolink: carica invece
krinker il

1
Qualcuno può confermare che l' "turbolinks:load"evento funziona in Rails 4.2? Il turbolinks:prefisso dell'evento è stato introdotto nel Nuovo Turbolink e non viene utilizzato in Rails 4.2 IIRC che utilizza Turbolink Classic . Prova a provare "page:change"se "turbolinks:load"non funziona sulla tua app pre-Rails 5. Vedi guide.rubyonrails.org/v4.2.7/…
Eliot Sykes

1
@EliotSykes La guida non è stata aggiornata. Rails 4.2 ora utilizza github.com/turbolinks/turbolinks invece di turbolink-classic. E in ogni caso, questo dipende da ciò che hai specificato nel tuo Gemfile. Spero che confermerai lo stesso presto :)
vedant

3
Grazie @ vedant1811. Al momento della stesura, sto confermando che una nuova app Rails 4.2.7 utilizza Turbolink 5 (non turbolink-classic). Sviluppatori che leggono questo: controlla Gemfile.lock per la versione di turbolink utilizzata. Se è inferiore a 5.0, utilizzare page:changeo aggiornare i turbolink. Anche trovato questo, può essere rilevante per alcuni, in cui i turbolink traducono gli eventi ai vecchi nomi degli eventi: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Eliot Sykes

1
La alert "page has loaded!"necessità di essere indentata per essere effettivamente eseguita dalla funzione di callback?
mbigras,

10

NOTA: vedere la risposta di @ SDP per una soluzione pulita e integrata

L'ho risolto come segue:

assicurati di includere application.js prima che gli altri file js dipendenti dall'app vengano inclusi modificando l'ordine di inclusione come segue:

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

Definire una funzione globale che gestisce l'associazione application.js

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

Ora puoi associare cose come:

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

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

2
Questo sembra il più pulito dal momento che devi solo aggiungere questo in un posto, non tutti i file coffeescript. Bel lavoro!
pyrospade,

@sled Quindi sta dicendo questo per usare il metodo SDP di usare la gemma e questo codice? O è una risposta che non usa la gemma?
ahnbizcad,

dimentica questa risposta e usa la soluzione di SDP.
slitta,

@sled È possibile combinare la soluzione SDP con l'idea di mettere la chiamata in un solo posto?
ahnbizcad,

1
@gwho: mostra la risposta di quanto SDP, turbolinks aggancia alla readymanifestazione, questo significa che è possibile utilizzare il modo "standard" di inizializzazione: $(document).ready(function() { /* do your init here */});. Il tuo codice init dovrebbe essere chiamato quando viene caricata la pagina completa (-> senza turbolink) e il tuo codice init dovrebbe essere eseguito di nuovo quando carichi una pagina tramite turbolink. Se vuoi eseguire un po 'di codice SOLO quando è stato utilizzato un turbolink:$(document).on('page:load', function() { /* init only for turbolinks */});
slitta,

8

Nessuna delle precedenti funzioni per me, ho risolto questo problema non utilizzando $ (document) di jQuery, ma invece ho usato addEventListener.

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

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

Questa è l'unica soluzione che funziona per me su ogni browser, per Turbolink> = 5 e che si attiva sia al caricamento della prima pagina che quando si fa clic su qualsiasi collegamento (con turbolink). Questo è il caso, perché mentre i trigger Chrome turbolinks:loadsul primo caricamento della pagina, Safari non e il modo hacky per risolvere il problema (trigger turbolinks:loadsu application.js), ovviamente, rompe Chrome (che spara due volte con questo). Questa è la risposta corretta Sicuramente andare con i 2 eventi readye turbolinks:load.
lucasarruda,



2

Invece di utilizzare una variabile per salvare la funzione "pronto" e associarla agli eventi, è possibile che si desideri attivare l' readyevento ogni volta che si page:loadattiva.

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

2

Ecco cosa ho fatto per assicurarmi che le cose non vengano eseguite due volte:

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

Trovo che usare la jquery-turbolinksgemma o combinare $(document).readye / $(document).on("page:load")o usare $(document).on("page:change")da solo si comporti inaspettatamente, specialmente se sei in fase di sviluppo.


Ti dispiace spiegare cosa intendi per comportamento inaspettato?
sunnyrjuneja,

Se ho solo un semplice avviso senza il $(document).offbit in quella "pagina: modifica" anonima sopra, ovviamente mi avviserà quando arrivo a quella pagina. Ma almeno nello sviluppo che esegue WEBrick, avviserà anche quando faccio clic su un collegamento a un'altra pagina. Evento peggiore, avvisa due volte quando faccio clic su un collegamento alla pagina originale.
Grey Kemmey,

2

Ho trovato il seguente articolo che ha funzionato benissimo per me e descrive in dettaglio l'uso di quanto segue:

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


2
$(document).ready(ready)  

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

2
Mentre questo frammento di codice può risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice.
andreas,

4
In realtà, questa risposta non è corretta. L' turbolinks:loadevento viene generato per il caricamento della pagina iniziale e dopo i seguenti collegamenti. Se si allega un evento sia all'evento standard readyche turbolinks:loadall'evento, verrà generato due volte quando si visita per la prima volta una pagina.
alexanderbird,

Questa risposta non è corretta Come ha detto @alexanderbird, provoca readydue volte il fuoco sui successivi caricamenti di pagina. Si prega di cambiarlo o rimuoverlo.
Chester

@alexanderbird Il tuo commento ha risolto uno dei problemi per me
Anwar,

1

Ho provato così tante soluzioni finalmente arrivate a questo. Tante volte il tuo codice non viene chiamato due volte.

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

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


1

Di solito faccio i seguenti progetti per i miei progetti su rotaie 4:

In application.js

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

Quindi nel resto dei file .js, invece di usare $(function (){})I callonInit(function(){})


0

Innanzitutto, installa jquery-turbolinksgem. E poi, non dimenticare di spostare i file Javascript inclusi dalla fine del tuo corpo application.html.erbal suo <head>.

Come descritto qui , se hai inserito il link javascript dell'applicazione nel piè di pagina per motivi di ottimizzazione della velocità, dovrai spostarlo nel tag in modo che venga caricato prima del contenuto nel tag. Questa soluzione ha funzionato per me.


0

Ho trovato le mie funzioni raddoppiate quando ho usato una funzione per readye turbolinks:loadquindi ho usato,

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

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

In questo modo le tue funzioni non raddoppieranno se i turbolink sono supportati!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.