Rails 4: jak używać $ (dokument) .ready () z turbo-linkami


422

Wystąpił problem w mojej aplikacji Rails 4, próbując uporządkować pliki JS „po torach”. Wcześniej były one rozrzucone po różnych widokach. Zorganizowałem je w osobne pliki i skompilowałem je z potokiem zasobów. Jednak właśnie dowiedziałem się, że „gotowe” zdarzenie jQuery nie uruchamia się przy kolejnych kliknięciach, gdy włączone jest łączenie turbo. Przy pierwszym załadowaniu strony działa. Ale kiedy klikniesz link, nic wewnątrz ready( function($) {nie zostanie wykonane (ponieważ strona nie ładuje się ponownie). Dobre wyjaśnienie: tutaj .

Więc moje pytanie brzmi: jaki jest właściwy sposób, aby upewnić się, że zdarzenia jQuery działają poprawnie, gdy włączone są łącza Turbo? Czy pakujesz skrypty w odbiornik specyficzny dla Railsów? A może szyny mają jakąś magię, która czyni ją niepotrzebną? Dokumenty są nieco niejasne, jak to powinno działać, szczególnie w odniesieniu do ładowania wielu plików za pośrednictwem manifestów, takich jak application.js.

Odpowiedzi:


554

Oto co robię ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

ostatnia linia nasłuchuje na ładowanie strony, co spowoduje wywołanie linków turbo.

Edytuj ... dodając wersję Javascript (na żądanie):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Edycja 2 ... Dla szyn 5 (Turbolinks 5) page:loadstaje się turbolinks:loadi będzie wystrzeliwany przy pierwszym załadowaniu. Możemy więc wykonać następujące czynności:

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

  ...your javascript goes here...

});

3
Czy to skrypt na kawę? Jeszcze się tego nie nauczyłem. Czy istnieje js lub jQuery równoważne z twoim przykładem?
emersonthis

6
Że mam go: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)Czy jest jakiś problem dla obu .ready i 'page:load'wyzwalane uzyskiwanie?
emersonthis

4
@Emerson jest bardzo przydatna strona internetowa o wyraźnej nazwie: js2coffee.org
MrYoshiji

16
Mogę potwierdzić, że funkcja gotowości nie zostanie wywołana dwukrotnie. Jeśli jest to trudne odświeżania, tylkoready zdarzenie jest zwolniony. Jeśli jest to obciążenie turbolinks, tylkopage:load zdarzenie jest zwolniony. Oba są niemożliwe readyi page:loadmożna je zwolnić na tej samej stronie.
Christian

7
Do Twojej wiadomości możesz także zastosować to w przypadku wielu zdarzeń page:loadi readydołączając ciąg rozdzielany spacją dla pierwszego argumentu. $(document).on('page:load ready', ready);Drugi argument to po prostu funkcja, która przypadkowo nazywa się readyzgodnie z przykładem tej odpowiedzi.
ahnbizcad

235

Właśnie dowiedziałem się o innej opcji rozwiązania tego problemu. Jeśli włożysz ten jquery-turbolinksgem będzie wiązać wydarzenia Szyny Turbolinks do document.readyzdarzeń, dzięki czemu można napisać jQuery w zwykły sposób. Po prostu dodajesz jquery.turbolinksbezpośrednio jqueryw pliku manifestu js (domyślnie:) application.js.


2
Wielkie dzieki! Właśnie tego potrzebuję. Droga po szynach. Konwencja.
Jeremy Becker,

6
Zgodnie z dokumentacją należy dodać //= require jquery.turbolinksdo manifestu (np. Application.js).
apocryphalauthor

1
@wildmonkey dwie odpowiedzi wykluczają się wzajemnie. 0.o
ahnbizcad

1
Po dodaniu tego klejnotu musisz ponownie uruchomić serwer Railsowy
Toby 1 Kenobi,

21
Pamiętaj, że Rails 4 domyślnie ma teraz Turbolinks 5, co z kolei nie jest obsługiwane przez jquery.turbolinks! Zobacz github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian

201

Ostatnio znalazłem najbardziej czysty i łatwy do zrozumienia sposób radzenia sobie z tym:

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

LUB

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

EDYCJA
Jeśli delegowałeś zdarzenia powiązane z document, upewnij się, że dołączasz je poza readyfunkcją, w przeciwnym razie odbijają się one przy każdym page:loadzdarzeniu (powodując wielokrotne uruchamianie tych samych funkcji). Na przykład, jeśli masz takie połączenia:

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

Wyjmij je z readyfunkcji, tak jak to:

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

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

Zdarzenia delegowane powiązane z tym wydarzeniem documentnie muszą być powiązane ready.


9
Jest to o wiele prostsze niż zaakceptowana odpowiedź utworzenia oddzielnej ready()funkcji. Premia do głosowania za wyjaśnienie wiążących zdarzeń delegowanych poza readyfunkcją.
Christian

1
Wadą tej metody jest to, że $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas

@Dezi @mfazekas Czy to rozwiązanie zadziała, jeśli używasz wcześniejszych wersji jQuery i BEZ klejnotu jquery-turbolinks? A może readyczęść została unieważniona przy użyciu klejnotu?
ahnbizcad

Najprostszy i najłatwiejszy sposób to zrobić. Obsługuje kod js dystrybuowany w wielu plikach i nie muszę się martwić, w jakiej kolejności zostały zawarte. Znacznie czystszy niż zmienne przypisanie.
Evgenia Manolova

3

91

Znalazłem to w dokumentacji Rails 4 , podobnej do rozwiązania DemoZluka, ale nieco krótszej:

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

LUB

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

Jeśli masz zewnętrzne skrypty, które wywołują $(document).ready()lub nie możesz niepokoić się przepisywaniem wszystkich istniejących skryptów JavaScript, ten klejnot pozwala ci nadal używać $(document).ready()TurboLink: https://github.com/kossnocorp/jquery.turbolinks


79

Zgodnie z nowymi przewodnikami po szynach poprawnym sposobem jest wykonanie następujących czynności:

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

lub, w coffeescript:

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

Nie słuchaj wydarzenia $(document).readyi zostanie uruchomione tylko jedno wydarzenie. Bez niespodzianek, bez potrzeby używania klejnotu jquery.turbolinks .

Działa to z szynami 4.2 i nowszymi, nie tylko z szynami 5.


Fajnie, to działało dla mnie. Wypróbowałem rozwiązanie tutaj: stackoverflow.com/questions/17881384/... ale z jakiegoś powodu nie działało. Teraz ładuję na turbolinkach: ładuj zamiast
krinker

1
Czy ktoś może potwierdzić, że "turbolinks:load"wydarzenie działa w Rails 4.2? turbolinks:Prefiks zdarzenia wprowadzono w Nowym Turbolinks i nie stosuje się szyn 4,2 IIRC który wykorzystuje Turbolinks Klasyczne . Zastanów się, "page:change"czy "turbolinks:load"nie działa w aplikacji wcześniejszej niż Rails 5. Patrz guide.rubyonrails.org/v4.2.7/…
Eliot Sykes,

1
@EliotSykes Przewodnik nie został zaktualizowany. Rails 4.2 używa teraz github.com/turbolinks/turbolinks zamiast turbolinks-classic. W każdym razie zależy to od tego, co określiłeś w swoim Gemfile. Mam nadzieję, że wkrótce to potwierdzisz :)
wedant

3
Dzięki @ vedant1811. W chwili pisania tego artykułu potwierdzam, że nowa aplikacja Rails 4.2.7 wykorzystuje Turbolinks 5 (nie turbolinks-classic). Programiści, którzy to czytają - sprawdź swoją Gemfile.lock pod kątem używanej wersji turbolinks. Jeśli jest mniejsza niż 5.0, użyj page:changelub zaktualizuj turbolinki. Stwierdzono również, że może to dotyczyć niektórych, w których turbolinks tłumaczy zdarzenia na stare nazwy zdarzeń: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Eliot Sykes

1
Czy alert "page has loaded!"konieczne jest wcięcie, aby faktycznie było uruchamiane przez funkcję wywołania zwrotnego?
mbigras

10

UWAGA: Zobacz @ SDP, aby uzyskać czyste, wbudowane rozwiązanie

Naprawiłem to w następujący sposób:

upewnij się, że dołączasz plik application.js, zanim inne pliki js zależne od aplikacji zostaną dołączone, zmieniając kolejność dołączania w następujący sposób:

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

Zdefiniuj funkcję globalną, która obsługuje wiązanie application.js

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

Teraz możesz wiązać takie rzeczy jak:

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

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

2
Wydaje się to najczystsze, ponieważ musisz dodać to tylko w jednym miejscu, a nie w każdym pliku coffeescript. Dobra robota!
pirospada

@ sled Czy to powiedzenie używa metody SDP użycia klejnotu i tego kodu? Czy jest to odpowiedź, która nie używa klejnotu?
ahnbizcad

zapomnij tę odpowiedź i skorzystaj z rozwiązania SDP.
sanki

@sled Czy można połączyć rozwiązanie SDP z koncepcją umieszczenia połączenia tylko w jednym miejscu?
ahnbizcad

1
@gwho: pokazy odpowiedź, SDP, w turbolinks haki do readywypadku, oznacza to, można użyć „standard” sposób inicjalizacji: $(document).ready(function() { /* do your init here */});. Twój kod inicjujący powinien być wywoływany, gdy ładowana jest cała strona (-> bez turbolinków), a kod inicjujący powinien być wykonywany ponownie, gdy ładujesz stronę za pomocą turbolinków. Jeśli chcesz wykonać kod TYLKO, gdy użyto turbolinka:$(document).on('page:load', function() { /* init only for turbolinks */});
sanie

8

Żadne z powyższych nie działa dla mnie, rozwiązałem to nie używając jQuery's $ (document) .ready, ale zamiast tego użyłem addEventListener.

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

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

To jedyne rozwiązanie, które działa dla mnie w każdej przeglądarce, dla Turbolinks> = 5 i które uruchamia się zarówno przy ładowaniu pierwszej strony, jak i po kliknięciu dowolnego łącza (z turbolinkami). Dzieje się tak, ponieważ chociaż Chrome uruchamia turbolinks:loadsię przy pierwszym ładowaniu strony, Safari nie działa, a hacky sposób, aby to naprawić ( turbolinks:loadwłączenie application.js) oczywiście psuje Chrome (który uruchamia się dwa razy z tym). To jest poprawna odpowiedź. Zdecydowanie skorzystaj z 2 wydarzeń readyi turbolinks:load.
lucasarruda



2

Zamiast używać zmiennej do zapisywania funkcji „gotowości” i wiązania jej ze zdarzeniami, możesz chcieć wywołać readyzdarzenie za każdym razem, gdy zostanie page:loadwyzwolone.

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

2

Oto, co zrobiłem, aby upewnić się, że rzeczy nie są wykonywane dwukrotnie:

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

Uważam, że używanie jquery-turbolinksklejnotu lub łączenie $(document).readyi / $(document).on("page:load")lub używanie $(document).on("page:change")zachowuje się nieoczekiwanie - szczególnie jeśli jesteś w fazie rozwoju.


Czy masz coś przeciwko wytłumaczeniu, co masz na myśli, zachowując się nieoczekiwanie?
sunnyrjuneja

Jeśli mam tylko prosty alert bez $(document).offfragmentu tej anonimowej funkcji „page: change” powyżej, to oczywiście zaalarmuję, kiedy dojdę do tej strony. Ale przynajmniej w fazie projektowania WEBrick będzie także ostrzegał, gdy kliknę link do innej strony. Gorzej, alarmuje dwa razy, kiedy kliknę link z powrotem do oryginalnej strony.
Gray Kemmey,

2

Znalazłem następujący artykuł, który był dla mnie świetny i szczegółowo opisałem następujące zastosowania:

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
Ten fragment kodu może rozwiązać pytanie, ale wyjaśnienie naprawdę pomaga poprawić jakość posta. Pamiętaj, że w przyszłości odpowiadasz na pytanie czytelników, a ci ludzie mogą nie znać przyczyn Twojej sugestii kodu.
andreas

4
W rzeczywistości ta odpowiedź jest nieprawidłowa. W turbolinks:loadZdarzenie dla wstępnego ładowania strony, jak również po wykonaniu połączeń. Jeśli dołączysz wydarzenie zarówno do readyzdarzenia standardowego , jak i turbolinks:loadwydarzenia, będzie ono uruchamiane dwukrotnie przy pierwszej wizycie na stronie.
alexanderbird,

Ta odpowiedź jest niepoprawna. Jak powiedział @alexanderbird, powoduje readydwukrotne uruchomienie przy kolejnych ładowaniach stron. Zmień lub usuń.
chester

@alexanderbird Twój komentarz rozwiązał dla mnie jeden problem
Anwar

1

Testowane tak wiele rozwiązań w końcu do tego doszło. Tyle twoich kodów zdecydowanie nie jest wywoływanych dwukrotnie.

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

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


1

Zazwyczaj wykonuję następujące czynności dla moich projektów z szynami 4:

W application.js

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

Następnie w pozostałych plikach .js zamiast za pomocą $(function (){})I callonInit(function(){})


0

Najpierw zainstaluj jquery-turbolinksklejnot. A potem nie zapomnij przenieść dołączonych plików JavaScript z końca swojego ciała application.html.erbdo jego <head>.

Jak opisano tutaj , jeśli umieścisz link javascript aplikacji w stopce ze względu na optymalizację prędkości, musisz przenieść go do tagu, aby załadował się przed zawartością w tagu. To rozwiązanie działało dla mnie.


0

Znalazłem moje funkcje podwoiła się podczas korzystania z funkcji dla readyi turbolinks:loadtak kiedyś,

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

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

W ten sposób twoje funkcje nie podwajają się, jeśli obsługiwane są turbolinki!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.