Schienen 4: Verwendung von $ (document) .ready () mit Turbo-Links


422

Ich bin auf ein Problem in meiner Rails 4-App gestoßen, als ich versucht habe, JS-Dateien "auf Rails-Weise" zu organisieren. Sie waren zuvor über verschiedene Ansichten verstreut. Ich habe sie in separaten Dateien organisiert und mit der Assets-Pipeline kompiliert. Ich habe jedoch gerade erfahren, dass das "ready" -Ereignis von jQuery bei nachfolgenden Klicks nicht ausgelöst wird, wenn die Turboverknüpfung aktiviert ist. Wenn Sie eine Seite zum ersten Mal laden, funktioniert sie. Wenn Sie jedoch auf einen Link klicken, wird nichts innerhalb des Links ready( function($) {ausgeführt (da die Seite nicht erneut geladen wird). Gute Erklärung: hier .

Meine Frage lautet also: Wie kann ich sicherstellen, dass jQuery-Ereignisse ordnungsgemäß funktionieren, während Turbo-Links aktiviert sind? Schließen Sie die Skripte in einen Rails-spezifischen Listener ein? Oder hat Rails vielleicht etwas Magie, die es unnötig macht? Die Dokumente sind etwas vage, wie dies funktionieren soll, insbesondere im Hinblick auf das Laden mehrerer Dateien über die Manifeste wie application.js.

Antworten:


554

Folgendes mache ich ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

Die letzte Zeile wartet auf das Laden der Seite, was durch Turbo-Links ausgelöst wird.

Bearbeiten ... Hinzufügen einer Javascript-Version (auf Anfrage):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Edit 2 ... For Rails 5 (Turbolinks 5) page:loadwird turbolinks:loadund wird sogar beim ersten Laden ausgelöst. Wir können also einfach Folgendes tun:

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

  ...your javascript goes here...

});

3
Ist das Kaffeeskript? Ich habe es noch nicht gelernt. Gibt es ein js oder jQuery, das Ihrem Beispiel entspricht?
Emersonthis

6
Denken Sie, ich habe es verstanden: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)Gibt es Bedenken für beide .ready und für 'page:load'das Auslösen?
Emersonthis

4
@ Emerson gibt es eine sehr nützliche Website mit einem expliziten Namen: js2coffee.org
MrYoshiji

16
Ich kann bestätigen, dass die Bereitschaftsfunktion nicht zweimal aufgerufen wird. Wenn es sich um eine harte Aktualisierung handelt, wird nur das readyEreignis ausgelöst. Wenn es sich um eine Turbolink-Ladung handelt, wird nur das page:loadEreignis ausgelöst. Es ist unmöglich für beide readyund page:loadauf derselben Seite gefeuert zu werden.
Christian

7
Zu Ihrer Information Sie können dies auch für mehrere Ereignisse anwenden page:loadund eine readydurch Leerzeichen getrennte Zeichenfolge für das erste Argumet einfügen. $(document).on('page:load ready', ready);Das zweite Argument ist einfach eine Funktion, die nach readydem Beispiel dieser Antwort benannt wird.
Ahnbizcad

235

Ich habe gerade von einer anderen Möglichkeit zur Lösung dieses Problems erfahren. Wenn Sie den jquery-turbolinksEdelstein laden , werden die Rails Turbolinks-Ereignisse an die document.readyEreignisse gebunden, sodass Sie Ihre jQuery auf die übliche Weise schreiben können. Sie fügen einfach jquery.turbolinksdirekt danach jqueryin die js-Manifestdatei ein (standardmäßig :) application.js.


2
Super, danke! Genau das, was ich brauche. Der Schienenweg. Konvention.
Jeremy Becker

6
Gemäß der Dokumentation sollten Sie //= require jquery.turbolinksdem Manifest hinzufügen (z. B. application.js).
Apokryphenautor

1
@wildmonkey Die beiden Antworten schließen sich gegenseitig aus. 0.o
ahnbizcad

1
Sie müssen Ihren Rails-Server neu starten, wenn Sie dieses Juwel hinzufügen
Toby 1 Kenobi

21
Bitte beachten Sie, dass Rails 4 jetzt standardmäßig Turbolinks 5 verwendet, was wiederum von jquery.turbolinks nicht unterstützt wird! Siehe github.com/kossnocorp/jquery.turbolinks/issues/56
Sebastian

201

Kürzlich fand ich die sauberste und leicht verständlichste Art, damit umzugehen:

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

ODER

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

BEARBEITEN
Wenn Sie Ereignisse delegiert haben , die an die gebunden sind document, stellen Sie sicher, dass Sie sie außerhalb der readyFunktion anhängen. Andernfalls werden sie bei jedem page:loadEreignis neu generiert (wodurch dieselben Funktionen mehrmals ausgeführt werden). Zum Beispiel, wenn Sie solche Anrufe haben:

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

Nehmen Sie sie wie folgt aus der readyFunktion heraus:

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

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

An das Ereignis gebundene delegierte Ereignisse documentmüssen nicht an das readyEreignis gebunden sein .


9
Dies ist viel einfacher als die akzeptierte Antwort, eine separate ready()Funktion zu erstellen . Bonus-Upvotes für die Erklärung verbindlicher delegierter Ereignisse außerhalb der readyFunktion.
Christian

1
Nachteil dieser Methode ist, dass $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas

@Dezi @mfazekas Würde diese Lösung funktionieren, wenn Sie frühere Versionen von jQuery und OHNE das Juwel jquery-turbolinks verwenden? Oder wird der readyTeil durch Verwendung des Edelsteins ungültig gemacht?
Ahnbizcad

Der einfachste und einfachste Weg, dies zu tun. Es unterstützt js Code, der in mehreren Dateien verteilt ist, und ich muss mich nicht darum kümmern, in welcher Reihenfolge sie enthalten waren. Viel sauberer als die Variablenzuordnung.
Evgenia Manolova

3
Sie sollten wahrscheinlich "page: change" anstelle von "page: load" verwenden, da erstere ausgelöst wird, unabhängig davon, ob die Seite vom Server oder vom clientseitigen Cache geladen wird .
Nathan Long

91

Fand dies in der Rails 4-Dokumentation , ähnlich der DemoZluk-Lösung, aber etwas kürzer:

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

ODER

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

Wenn Sie externe Skripte haben, die aufrufen, $(document).ready()oder wenn Sie nicht die Mühe haben, Ihr gesamtes vorhandenes JavaScript neu zu schreiben, können Sie dieses Juwel weiterhin $(document).ready()mit TurboLinks verwenden: https://github.com/kossnocorp/jquery.turbolinks


79

Gemäß den neuen Schienenführungen ist der richtige Weg, Folgendes zu tun:

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

oder in Kaffeeskript:

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

Hören Sie nicht auf das Ereignis $(document).readyund es wird nur ein Ereignis ausgelöst. Keine Überraschungen, keine Notwendigkeit, das Juwel jquery.turbolinks zu verwenden .

Dies funktioniert mit Schienen 4.2 und höher, nicht nur mit Schienen 5.


schön das hat bei mir geklappt. Versuchte Lösung hier: stackoverflow.com/questions/17881384/… hat aber aus irgendeinem Grund nicht funktioniert. Jetzt lade ich auf Turbolinks: stattdessen laden
Krinker

1
Kann jemand bestätigen, dass das "turbolinks:load"Ereignis in Rails 4.2 funktioniert? Das turbolinks:Ereignispräfix wurde in den neuen Turbolinks eingeführt und wird in Rails 4.2 IIRC, das Turbolinks Classic verwendet, nicht verwendet . Versuchen Sie es, "page:change"wenn "turbolinks:load"Ihre Pre-Rails 5-App nicht funktioniert. Siehe guides.rubyonrails.org/v4.2.7/...
Eliot Sykes

1
@EliotSykes Der Leitfaden wurde nicht aktualisiert. Rails 4.2 verwendet jetzt github.com/turbolinks/turbolinks anstelle von turbolinks-classic. Dies hängt in jedem Fall davon ab, was Sie in Ihrer Gemfile angegeben haben. Hoffe, Sie werden das gleiche bald bestätigen :)
vedant

3
Danke @ vedant1811. Zum Zeitpunkt des Schreibens bestätige ich, dass eine neue Rails 4.2.7-App Turbolinks 5 verwendet (kein Turbolinks-Klassiker). Entwickler, die dies lesen - überprüfen Sie Ihre Gemfile.lock auf die verwendete Turbolink-Version. Wenn es weniger als 5.0 ist, verwenden page:changeoder aktualisieren Sie Turbolinks. Auch gefunden, kann dies für einige relevant sein, wo Turbolinks Ereignisse in die alten Ereignisnamen übersetzt: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Eliot Sykes

1
Muss das alert "page has loaded!"eingerückt werden, um tatsächlich von der Rückruffunktion ausgeführt zu werden?
Mbigras

10

HINWEIS: Eine saubere, integrierte Lösung finden Sie in der Antwort von @ SDP

Ich habe es wie folgt behoben:

Stellen Sie sicher, dass Sie application.js einschließen, bevor die anderen app-abhängigen js-Dateien aufgenommen werden, indem Sie die Einschlussreihenfolge wie folgt ändern:

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

Definieren Sie eine globale Funktion, die die Bindung in verarbeitet application.js

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

Jetzt können Sie Dinge binden wie:

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

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

2
Dies scheint am saubersten zu sein, da Sie dies nur an einer Stelle hinzufügen müssen, nicht in jeder Coffeescript-Datei. Gute Arbeit!
Pyrospade

@sled Soll dieses Sprichwort also die SDP-Methode zur Verwendung des Edelsteins und dieses Codes verwenden? Oder ist dies eine Antwort, die den Edelstein nicht verwendet?
Ahnbizcad

Vergessen Sie diese Antwort und verwenden Sie die SDP-Lösung.
Schlitten

@sled Ist es möglich, die SDP-Lösung mit dem Gedanken zu kombinieren, den Anruf nur an einem Ort zu tätigen?
Ahnbizcad

1
@gwho: Wie die Antwort von SDP zeigt, schließen sich Turbolinks an das readyEreignis an. Dies bedeutet, dass Sie die "Standard" -Methode zum Initialisieren verwenden können : $(document).ready(function() { /* do your init here */});. Ihr Init-Code sollte aufgerufen werden, wenn die gesamte Seite geladen ist (-> ohne Turbolinks), und Ihr Init-Code sollte erneut ausgeführt werden, wenn Sie eine Seite über Turbolinks laden. Wenn Sie einen Code NUR ausführen möchten, wenn ein Turbolink verwendet wurde:$(document).on('page:load', function() { /* init only for turbolinks */});
Schlitten

8

Keines der oben genannten Verfahren funktioniert für mich. Ich habe dieses Problem gelöst, indem ich nicht jQuerys $ (document) .ready verwendet habe, sondern stattdessen addEventListener verwendet habe.

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

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

Dies ist die einzige Lösung, die für mich in jedem Browser funktioniert, für Turbolinks> = 5, und die sowohl beim Laden der ersten Seite als auch beim Klicken auf Links (mit Turbolinks) ausgelöst wird. Das ist der Fall , weil während Chrome Trigger turbolinks:loadauf der ersten Seite zu laden, Safari nicht und den hacky Weg , es zu beheben (Triggerung turbolinks:loadauf application.js) offensichtlich bricht Chrome (die zweimal mit diesem feuert). Dies ist die richtige Antwort. Gehen Sie auf jeden Fall mit den 2 Veranstaltungen readyund turbolinks:load.
Lucasarruda



2

Anstatt eine Variable zu verwenden, um die Funktion "Bereit" zu speichern und an die Ereignisse zu binden, möchten Sie das Ereignis möglicherweise readybei jedem Auslösen auslösen page:load.

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

2

Folgendes habe ich getan, um sicherzustellen, dass die Dinge nicht zweimal ausgeführt werden:

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

Ich finde, dass sich das Verwenden des jquery-turbolinksEdelsteins oder das Kombinieren $(document).readyund / $(document).on("page:load")oder Verwenden $(document).on("page:change")von sich selbst unerwartet verhält - insbesondere, wenn Sie sich in der Entwicklung befinden.


Haben Sie etwas dagegen zu erklären, was Sie unter unerwartetem Verhalten verstehen?
Sunnyrjuneja

Wenn ich nur eine einfache Warnung ohne das $(document).offBit in dieser anonymen Funktion "Seite: Ändern" oben habe, wird sie offensichtlich benachrichtigt, wenn ich zu dieser Seite komme. Aber zumindest in der Entwicklung, in der WEBrick ausgeführt wird, wird eine Warnung ausgegeben, wenn ich auf einen Link zu einer anderen Seite klicke. Schlimmer noch, es wird zweimal benachrichtigt, wenn ich dann auf einen Link zurück zur Originalseite klicke.
Gray Kemmey

2

Ich habe den folgenden Artikel gefunden, der für mich hervorragend funktioniert hat, und die Verwendung des folgenden Artikels detailliert beschrieben:

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
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Andreas

4
Eigentlich ist diese Antwort falsch. Das turbolinks:loadEreignis wird beim ersten Laden der Seite sowie nach dem Folgen von Links ausgelöst. Wenn Sie ein Ereignis sowohl an das Standardereignis readyals auch an das turbolinks:loadEreignis anhängen , wird es beim ersten Besuch einer Seite zweimal ausgelöst.
Alexanderbird

Diese Antwort ist falsch. Wie @alexanderbird sagte, wird es readybeim nachfolgenden Laden der Seite zweimal ausgelöst . Bitte ändern oder entfernen Sie es.
Chester

@alexanderbird Ihr Kommentar löste eines der Probleme für mich
Anwar

1

Getestet kamen so viele Lösungen schließlich dazu. So viele dein Code wird definitiv nicht zweimal aufgerufen.

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

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


1

Normalerweise mache ich für meine Rails 4-Projekte Folgendes:

Im application.js

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

Dann in den restlichen .js-Dateien, anstatt zu verwenden, $(function (){})rufe ich aufonInit(function(){})


0

Installieren Sie zuerst jquery-turbolinksgem. Und vergessen Sie dann nicht, Ihre enthaltenen Javascript-Dateien vom Ende Ihres Körpers auf das application.html.erbzu verschieben <head>.

Wenn Sie, wie hier beschrieben , den Javascript-Link der Anwendung aus Gründen der Geschwindigkeitsoptimierung in die Fußzeile eingefügt haben, müssen Sie ihn in das Tag verschieben, damit er vor dem Inhalt des Tags geladen wird. Diese Lösung hat bei mir funktioniert.


0

Ich fand meine Funktionen verdoppelt , wenn eine Funktion für die Verwendung readyund turbolinks:loadso habe ich,

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

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

Auf diese Weise verdoppeln sich Ihre Funktionen nicht, wenn Turbolinks unterstützt werden!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.