Rails 4: comment utiliser $ (document) .ready () avec des turbo-liens

422

J'ai rencontré un problème dans mon application Rails 4 en essayant d'organiser les fichiers JS "à la manière des rails". Ils étaient auparavant dispersés dans différentes vues. Je les ai organisés dans des fichiers séparés et les compile avec le pipeline d'actifs. Cependant, je viens d'apprendre que l'événement "prêt" de jQuery ne se déclenche pas lors des clics suivants lorsque la liaison est activée. La première fois que vous chargez une page, cela fonctionne. Mais lorsque vous cliquez sur un lien, rien à l'intérieur de ready( function($) {ne sera exécuté (car la page ne se charge pas à nouveau). Bonne explication: ici .

Ma question est donc la suivante: quelle est la bonne façon de s'assurer que les événements jQuery fonctionnent correctement lorsque les turbo-liens sont activés? Enveloppez-vous les scripts dans un écouteur spécifique à Rails? Ou peut-être que les rails ont une magie qui les rend inutiles? Les documents sont un peu vagues sur la façon dont cela devrait fonctionner, en particulier en ce qui concerne le chargement de plusieurs fichiers via le (s) manifeste (s) comme application.js.

emersonthis
la source

Réponses:

554

Voici ce que je fais ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

la dernière ligne écoute le chargement de la page, ce qui déclenchera les liens turbo.

Modifier ... en ajoutant la version Javascript (par demande):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Edit 2 ... For Rails 5 (Turbolinks 5) page:loaddevient turbolinks:loadet sera même déclenché à la charge initiale. Nous pouvons donc simplement faire ce qui suit:

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

  ...your javascript goes here...

});
Meltemi
la source
3
C'est le scénario du café? Je ne l'ai pas encore appris. Existe-t-il un js ou jQuery équivalent à votre exemple?
emersonthis
6
Je pense que j'ai compris: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)y a-t-il une préoccupation pour les deux .ready et pour 'page:load'être déclenché?
emersonthis
4
@Emerson, il existe un site Web très utile, avec un nom explicite: js2coffee.org
MrYoshiji
16
Je peux confirmer que la fonction prête n'est pas appelée deux fois. S'il s'agit d'un rafraîchissement brutal, seul l' readyévénement est déclenché. S'il s'agit d'une charge de turboliens, seul l' page:loadévénement est déclenché. Il est impossible pour les deux readyet page:loadd'être renvoyé sur la même page.
Christian
7
FYI Vous pouvez également appliquer cela pour plusieurs événements page:loadet readyen incluant une chaîne séparée par des espaces pour le premier argument. $(document).on('page:load ready', ready);Le deuxième argument est simplement une fonction, qui se trouve être nommée ready, en suivant l'exemple de cette réponse.
ahnbizcad
235

Je viens d'apprendre une autre option pour résoudre ce problème. Si vous chargez la jquery-turbolinksgemme, elle liera les événements Rails Turbolinks aux document.readyévénements afin que vous puissiez écrire votre jQuery de la manière habituelle. Vous venez d'ajouter jquery.turbolinksjuste après jquerydans le fichier manifeste js (par défaut:) application.js.

emersonthis
la source
2
Super merci! Exactement ce dont j'ai besoin. La voie des rails. Convention.
Jeremy Becker
6
Selon la documentation , vous devez ajouter //= require jquery.turbolinksau manifeste (par exemple application.js).
apocryphalauthor
1
@wildmonkey les deux réponses s'excluent mutuellement. 0.o
ahnbizcad
1
Vous devez redémarrer votre serveur de rails lorsque vous ajoutez ce joyau
Toby 1 Kenobi
21
Veuillez noter que Rails 4 est désormais par défaut sur Turbolinks 5 qui, à son tour, n'est pas pris en charge par jquery.turbolinks! Voir github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian
201

Récemment, j'ai trouvé le moyen le plus propre et le plus facile à comprendre:

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

OU

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

EDIT
Si vous avez des événements délégués liés à l ' document, assurez-vous de les attacher en dehors de la readyfonction, sinon ils seront rebondis sur chaque page:loadévénement (provoquant l'exécution des mêmes fonctions plusieurs fois). Par exemple, si vous avez des appels comme celui-ci:

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

Retirez-les de la readyfonction, comme ceci:

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

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

Les événements délégués liés au documentne doivent pas nécessairement être liés à l' readyévénement.

Artyom Tsoy
la source
9
C'est beaucoup plus simple que la réponse acceptée de créer une ready()fonction distincte . Bonus de votes positifs pour l'explication des événements délégués contraignants en dehors de la readyfonction.
Christian
1
Le désavantage de cette méthode est que $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas
@Dezi @mfazekas Cette solution fonctionnerait-elle si vous utilisez des versions antérieures de jQuery et SANS la gemme jquery-turbolinks? Ou la readyportion est-elle invalide en utilisant la gemme?
ahnbizcad
La façon la plus simple et la plus simple de le faire. Il prend en charge le code js distribué dans plusieurs fichiers et je n'ai pas à me soucier de l'ordre dans lequel ils ont été inclus. Beaucoup plus propre que l'affectation des variables.
Evgenia Manolova
3
Vous devriez probablement utiliser «page: change» au lieu de «page: charger», car le premier est déclenché, que la page soit chargée à partir du serveur ou du cache côté client .
Nathan Long
91

Trouvé dans la documentation de Rails 4 , similaire à la solution de DemoZluk mais légèrement plus courte:

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

OU

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

Si vous avez des scripts externes qui appellent $(document).ready()ou si vous ne pouvez pas être dérangé par la réécriture de tout votre JavaScript existant, ce joyau vous permet de continuer à utiliser $(document).ready()avec TurboLinks: https://github.com/kossnocorp/jquery.turbolinks

migu
la source
79

Selon les nouveaux guides de rails , la bonne façon est de procéder comme suit:

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

ou, en coffeescript:

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

N'écoutez pas l'événement $(document).readyet un seul événement sera déclenché. Pas de surprise, pas besoin d'utiliser le joyau jquery.turbolinks .

Cela fonctionne avec les rails 4.2 et supérieurs, pas seulement les rails 5.

vedant
la source
bien, cela a fonctionné pour moi. Solution essayée ici: stackoverflow.com/questions/17881384/… mais n'a pas fonctionné pour une raison quelconque. Maintenant je charge sur turbolinks: charge à la place
krinker
1
Quelqu'un peut-il confirmer que l' "turbolinks:load"événement fonctionne dans Rails 4.2? Le turbolinks:préfixe d'événement a été introduit dans les nouveaux Turbolinks et n'est pas utilisé dans Rails 4.2 IIRC qui utilise Turbolinks Classic . Pensez à essayer "page:change"si "turbolinks:load"cela ne fonctionne pas sur votre application pré-Rails 5. Voir guides.rubyonrails.org/v4.2.7/…
Eliot Sykes
1
@EliotSykes Le guide n'a pas été mis à jour. Rails 4.2 utilise désormais github.com/turbolinks/turbolinks au lieu de turbolinks-classic. Et en tout cas, cela dépend de ce que vous avez spécifié dans votre Gemfile. J'espère que vous confirmerez bientôt la même chose :)
vedant
3
Merci @ vedant1811. Au moment de la rédaction, je confirme qu'une nouvelle application Rails 4.2.7 utilise Turbolinks 5 (pas turbolinks-classic). Développeurs lisant ceci - vérifiez votre Gemfile.lock pour la version turbolinks utilisée. Si elle est inférieure à 5,0, utilisez page:changeou mettez à niveau les turboliens. Également trouvé cela, peut être pertinent pour certains, où turbolinks traduit les événements en anciens noms d'événement: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Eliot Sykes
1
Le alert "page has loaded!"besoin doit-il être mis en retrait pour être réellement exécuté par la fonction de rappel?
mbigras
10

REMARQUE: Voir la réponse de @ SDP pour une solution propre et intégrée

Je l'ai corrigé comme suit:

assurez-vous d'inclure application.js avant que les autres fichiers js dépendants de l'application ne soient inclus en modifiant l'ordre d'inclusion comme suit:

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

Définissez une fonction globale qui gère la liaison dans application.js

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

Maintenant, vous pouvez lier des trucs comme:

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

// equivalent in javascript:
onLoad(function() {
  $('a.clickable').click(function() {
    alert('link clicked');
});
traîneau
la source
2
Cela semble le plus propre car vous n'avez qu'à l'ajouter en un seul endroit, pas tous les fichiers coffeescript. Bon travail!
pyrospade
@sled Est-ce donc ce dicton d'utiliser la méthode SDP pour utiliser la gemme et ce code? Ou est-ce une réponse qui n'utilise pas la gemme?
ahnbizcad
oubliez cette réponse et utilisez la solution SDP.
traîneau
@sled Est-il possible de combiner la solution de SDP avec la notion de placer l'appel uniquement en un seul endroit?
ahnbizcad
1
@gwho: spectacles de réponse de SDP Comme, turbolinks crochets jusqu'à l' readyévénement, cela signifie que vous pouvez utiliser comme « standard » d'initialisation: $(document).ready(function() { /* do your init here */});. Votre code init doit être appelé lorsque la page complète est chargée (-> sans turbolinks) et votre code init doit être exécuté à nouveau lorsque vous chargez une page via turbolinks. Si vous voulez exécuter du code UNIQUEMENT quand un turbolink a été utilisé:$(document).on('page:load', function() { /* init only for turbolinks */});
traîneau
8

Rien de ce qui précède ne fonctionne pour moi, j'ai résolu cela en n'utilisant pas $ (document) .ready de jQuery, mais en utilisant addEventListener à la place.

document.addEventListener("turbolinks:load", function() {
  // do something
});
RockL
la source
7
$(document).on 'ready turbolinks:load', ->
  console.log '(document).turbolinks:load'
Sukeerthi Adiga
la source
C'est la seule solution qui fonctionne pour moi sur tous les navigateurs, pour Turbolinks> = 5 et qui se déclenche à la fois lors du chargement de la première page et également lorsque vous cliquez sur des liens (avec des turbolinks). Tel est le cas parce que si Chrome déclencheurs turbolinks:loadsur la première page de chargement, Safari ne fonctionne pas et la façon hacky pour y remédier (déclenchement turbolinks:loadsur application.js) brise évidemment Chrome (qui se déclenche deux fois avec cela). Ceci est la bonne réponse. Allez-y avec les 2 événements readyet turbolinks:load.
lucasarruda
5

Vous devez utiliser:

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

de turbolinks doc .

Gian Franco Fioriello
la source
2

Au lieu d'utiliser une variable pour enregistrer la fonction "prête" et la lier aux événements, vous souhaiterez peut-être déclencher l' readyévénement chaque fois qu'il se page:loaddéclenche.

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

Voici ce que j'ai fait pour m'assurer que les choses ne sont pas exécutées deux fois:

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

Je trouve que l'utilisation de la jquery-turbolinksgemme ou la combinaison $(document).readyet / $(document).on("page:load")ou l'utilisation $(document).on("page:change")par elle-même se comporte de manière inattendue - surtout si vous êtes en développement.

Grey Kemmey
la source
Cela vous dérange-t-il d'expliquer ce que vous entendez par un comportement inattendu?
sunnyrjuneja
Si j'ai juste une simple alerte sans le $(document).offbit dans cette fonction anonyme "page: change" au-dessus, il sera évidemment alerté quand j'arriverai à cette page. Mais au moins dans le développement sous WEBrick, il sera également alerté lorsque je clique sur un lien vers une autre page. Événement pire, il alerte deux fois lorsque je clique ensuite sur un lien vers la page d'origine.
Gray Kemmey
2

J'ai trouvé l' article suivant qui a très bien fonctionné pour moi et détaille l'utilisation des éléments suivants:

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

Je pensais que je laisserais cela ici pour ceux qui passent à Turbolinks 5 : la façon la plus simple de corriger votre code est de:

var ready;
ready = function() {
  // Your JS here
}
$(document).ready(ready);
$(document).on('page:load', ready)

à:

var ready;
ready = function() {
  // Your JS here
}
$(document).on('turbolinks:load', ready);

Référence: https://github.com/turbolinks/turbolinks/issues/9#issuecomment-184717346

tirdadc
la source
2
$(document).ready(ready)  

$(document).on('turbolinks:load', ready)
Konstantinos Mou
la source
2
Bien que cet extrait de code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
andreas
4
En fait, cette réponse est incorrecte. L' turbolinks:loadévénement se déclenche pour le chargement initial de la page ainsi qu'après les liens suivants. Si vous attachez un événement à la fois à l' readyévénement standard et à l' turbolinks:loadévénement, il se déclenchera deux fois lors de votre première visite sur une page.
alexanderbird
Cette réponse est incorrecte. Comme l'a dit @alexanderbird, il readyse déclenche deux fois lors des chargements de page suivants. Veuillez le modifier ou le supprimer.
chester
@alexanderbird Votre commentaire a résolu l'un des problèmes pour moi
Anwar
1

Tester tant de solutions est finalement arrivé à cela. Ce nombre de votre code n'est certainement pas appelé deux fois.

      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
la source
1

Je fais généralement ce qui suit pour mes projets rails 4:

Dans application.js

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

Ensuite, dans le reste des fichiers .js, au lieu d'utiliser $(function (){})j'appelleonInit(function(){})

muZk
la source
0

Tout d'abord, installez jquery-turbolinksgem. Et puis, n'oubliez pas de déplacer vos fichiers Javascript inclus de la fin du corps de votre application.html.erbvers son <head>.

Comme décrit ici , si vous avez mis le lien javascript de l'application dans le pied de page pour des raisons d'optimisation de la vitesse, vous devrez le déplacer dans la balise afin qu'il se charge avant le contenu de la balise. Cette solution a fonctionné pour moi.

Aboozar Rajabi
la source
0

J'ai trouvé mes fonctions doublées lors de l'utilisation d'une fonction pour readyet turbolinks:loadj'ai donc utilisé,

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 cette façon, vos fonctions ne doublent pas si les turboliens sont pris en charge!

Lee Eather
la source