Rails 4: cara menggunakan $ (dokumen) .ready () dengan turbo-links

422

Saya mengalami masalah dalam aplikasi Rails 4 saya ketika mencoba mengatur file JS "the rails way". Mereka sebelumnya tersebar di berbagai pandangan. Saya mengorganisasikannya ke dalam file terpisah dan mengkompilasinya dengan pipa aset. Namun, saya baru tahu bahwa acara "siap" jQuery tidak memunculkan klik berikutnya saat penautan turbo dihidupkan. Pertama kali Anda memuat halaman itu berfungsi. Tetapi ketika Anda mengklik sebuah tautan, apa pun di dalamnya ready( function($) {tidak akan dieksekusi (karena halaman tersebut tidak benar-benar dimuat lagi). Penjelasan yang bagus: di sini .

Jadi pertanyaan saya adalah: Apa cara yang tepat untuk memastikan bahwa acara jQuery bekerja dengan baik saat tautan turbo aktif? Apakah Anda membungkus skrip dalam pendengar khusus Rails? Atau mungkin rel memiliki sihir yang membuatnya tidak perlu? Dokumen agak kabur tentang bagaimana ini seharusnya bekerja, terutama berkenaan dengan memuat banyak file melalui manifes (s) seperti application.js.

emersonthis
sumber

Jawaban:

554

Inilah yang saya lakukan ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

baris terakhir mendengarkan pemuatan halaman yang akan memicu tautan turbo.

Edit ... menambahkan versi Javascript (per permintaan):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Sunting 2 ... Untuk Rails 5 (Turbolinks 5) page:loadmenjadi turbolinks:loaddan bahkan akan diaktifkan pada beban awal. Jadi kita bisa melakukan hal berikut:

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

  ...your javascript goes here...

});
Meltemi
sumber
3
Apakah itu skrip kopi? Saya belum mempelajarinya. Apakah ada js atau jQuery yang setara dengan contoh Anda?
emersonthis
6
Pikir saya mengerti: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)Apakah ada kekhawatiran untuk keduanya .ready dan 'page:load'dipicu?
emersonthis
4
@Emerson ada situs web yang sangat berguna, dengan nama eksplisit: js2coffee.org
MrYoshiji
16
Saya dapat mengkonfirmasi bahwa fungsi siap tidak dipanggil dua kali. Jika itu refresh keras, hanya dengan readyacara dipecat. Jika beban turbolinks, hanya dengan page:loadacara dipecat. Tidak mungkin untuk keduanya readydan page:loaddipecat pada halaman yang sama.
Christian
7
FYI Anda juga dapat menerapkan ini untuk beberapa acara page:loaddan readydengan memasukkan string yang dipisahkan spasi untuk argumet pertama. $(document).on('page:load ready', ready);Argumen kedua hanyalah sebuah fungsi, yang kebetulan diberi nama ready, mengikuti contoh jawaban ini.
ahnbizcad
235

Saya baru tahu opsi lain untuk menyelesaikan masalah ini. Jika Anda memuat satu jquery-turbolinkspermata itu akan mengikat peristiwa Rails Turbolinks ke document.readyacara sehingga Anda dapat menulis jQuery Anda dengan cara yang biasa. Anda cukup menambahkan jquery.turbolinkstepat setelah jquerydalam file mans js (secara default:) application.js.

emersonthis
sumber
2
Keren terima kasih! Apa yang saya butuhkan. Cara rel. Konvensi.
Jeremy Becker
6
Menurut dokumentasi , Anda harus menambahkan //= require jquery.turbolinksmanifes (mis. Application.js).
apocryphalauthor
1
@wildmonkey kedua jawaban itu saling eksklusif. 0.o
ahnbizcad
1
Anda harus memulai ulang server rel Anda saat menambahkan permata ini
Toby 1 Kenobi
21
Harap dicatat bahwa Rails 4 sekarang default ke Turbolinks 5 yang, pada gilirannya, tidak didukung oleh jquery.turbolinks! Lihat github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian
201

Baru-baru ini saya menemukan cara yang paling bersih dan mudah dipahami untuk menghadapinya:

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

ATAU

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

EDIT
Jika Anda telah mendelegasikan acara yang terikat pada document, pastikan Anda melampirkannya di luar readyfungsi, jika tidak mereka akan mendapatkan pantulan pada setiap page:loadacara (menyebabkan fungsi yang sama dijalankan beberapa kali). Misalnya, jika Anda memiliki panggilan seperti ini:

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

Keluarkan mereka dari readyfungsi, seperti ini:

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

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

Acara yang didelegasikan terikat dengan documenttidak perlu terikat pada readyacara tersebut.

Artyom Tsoy
sumber
9
Ini jauh lebih sederhana daripada jawaban yang diterima untuk membuat ready()fungsi terpisah . Bonus upvotes untuk penjelasan mengikat acara yang didelegasikan di luar readyfungsi.
Christian
1
Kekurangan dari metode ini adalah $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas
@Dezi @mfazekas Apakah solusi ini berfungsi jika Anda menggunakan versi jQuery sebelumnya, dan TANPA permata jquery-turbolinks? Atau apakah readyporsinya dibuat tidak valid dengan menggunakan permata?
ahnbizcad
Cara termudah dan termudah untuk melakukannya. Ini mendukung kode js didistribusikan dalam beberapa file dan saya tidak perlu repot-repot dalam urutan mana mereka dimasukkan Jauh lebih bersih daripada penugasan variabel.
Evgenia Manolova
3
Anda mungkin harus menggunakan "page: change" daripada "page: load", karena yang pertama dipecat apakah halaman dimuat dari server atau dari cache sisi klien .
Nathan Long
91

Menemukan ini di dokumentasi Rails 4 , mirip dengan solusi DemoZluk tetapi sedikit lebih pendek:

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

ATAU

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

Jika Anda memiliki skrip eksternal yang memanggil $(document).ready()atau jika Anda tidak dapat repot menulis ulang semua JavaScript yang ada, maka permata ini memungkinkan Anda untuk tetap menggunakan $(document).ready()TurboLinks: https://github.com/kossnocorp/jquery.turbolinks

migu
sumber
79

Sesuai panduan rel baru , cara yang benar adalah dengan melakukan hal berikut:

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

atau, dalam naskah kopi:

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

Jangan dengarkan acara $(document).readydan hanya satu acara yang akan dipecat. Tidak ada kejutan, tidak perlu menggunakan permata jquery.turbolinks .

Ini bekerja dengan rel 4.2 dan di atas, tidak hanya rel 5.

vedant
sumber
bagus ini bekerja untuk saya. Mencoba solusi di sini: stackoverflow.com/questions/17881384/… tetapi tidak berhasil karena beberapa alasan. Sekarang saya memuat di turbolinks: memuat sebagai gantinya
krinker
1
Adakah yang bisa mengonfirmasi bahwa "turbolinks:load"acara bekerja di Rails 4.2? The turbolinks:Acara awalan diperkenalkan di New Turbolinks dan tidak digunakan dalam Rails 4.2 IIRC yang menggunakan Turbolinks Klasik . Pertimbangkan untuk mencoba "page:change"jika "turbolinks:load"tidak bekerja pada aplikasi pre-Rails 5 Anda. Lihat panduan.rubyonrails.org/v4.2.7/…
Eliot Sykes
1
@EliotSykes Panduan ini belum diperbarui. Rails 4.2 sekarang menggunakan github.com/turbolinks/turbolinks sebagai ganti turbolinks-classic. Dan bagaimanapun, ini tergantung pada apa yang telah Anda tentukan di Gemfile Anda. Semoga Anda akan segera mengonfirmasi hal yang sama :)
vedant
3
Terima kasih @ vedant1811. Pada saat penulisan, saya mengonfirmasi bahwa aplikasi Rails 4.2.7 baru menggunakan Turbolinks 5 (bukan turbolinks-classic). Pengembang membaca ini - periksa Gemfile Anda. Kunci untuk versi turbolink yang digunakan. Jika kurang dari 5.0 maka gunakan page:changeatau perbarui turbolinks. Juga menemukan ini, mungkin relevan bagi sebagian orang, di mana turbolink menerjemahkan acara ke nama acara lama: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Eliot Sykes
1
Apakah alert "page has loaded!"kebutuhan untuk membuat indentasi benar-benar dijalankan oleh fungsi panggilan balik?
mbigras
10

CATATAN: Lihat jawaban @ SDP untuk solusi yang bersih dan terintegrasi

Saya memperbaikinya sebagai berikut:

pastikan Anda menyertakan application.js sebelum file js yang bergantung pada aplikasi lainnya disertakan dengan mengubah urutan sertakan sebagai berikut:

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

Tetapkan fungsi global yang menangani pengikatan application.js

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

Sekarang Anda dapat mengikat hal-hal seperti:

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

// equivalent in javascript:
onLoad(function() {
  $('a.clickable').click(function() {
    alert('link clicked');
});
kereta luncur
sumber
2
Ini tampaknya paling bersih karena Anda hanya perlu menambahkan ini di satu tempat, bukan setiap file naskah. Kerja bagus!
pyrospade
@sled Jadi ini pepatah untuk menggunakan metode SDP menggunakan permata dan kode ini? Atau apakah ini jawaban yang tidak menggunakan permata?
ahnbizcad
lupakan jawaban ini dan gunakan solusi SDP.
kereta luncur
@sled Apakah mungkin untuk menggabungkan solusi SDP dengan gagasan menempatkan panggilan hanya di satu tempat?
ahnbizcad
1
@gwho: Sebagai SDP jawaban menunjukkan, turbolinks kait sampai ke readyacara, berarti ini Anda dapat menggunakan "standar" cara menginisialisasi: $(document).ready(function() { /* do your init here */});. Kode init Anda harus dipanggil ketika halaman lengkap dimuat (-> tanpa turbolinks) dan kode init Anda harus dieksekusi lagi ketika Anda memuat halaman melalui turbolinks. Jika Anda ingin menjalankan beberapa kode SAJA ketika turbolink telah digunakan:$(document).on('page:load', function() { /* init only for turbolinks */});
sled
8

Tidak ada satu pun di atas yang berfungsi untuk saya, saya menyelesaikan ini dengan tidak menggunakan $ (dokumen) jQuery. Sudah, tetapi gunakan addEventListener sebagai gantinya.

document.addEventListener("turbolinks:load", function() {
  // do something
});
RockL
sumber
7
$(document).on 'ready turbolinks:load', ->
  console.log '(document).turbolinks:load'
Sukeerthi Adiga
sumber
Itu adalah satu-satunya solusi yang berfungsi untuk saya di setiap browser, untuk Turbolinks> = 5 dan yang memicu keduanya pada pemuatan halaman pertama dan juga ketika Anda mengklik tautan apa pun (dengan turbolinks). Itu sebabnya karena ketika Chrome memicu turbolinks:loadpada halaman pertama dimuat, Safari tidak dan cara hacky untuk memperbaikinya (memicu turbolinks:loadaktif application.js) jelas memecah Chrome (yang menyala dua kali dengan ini). Ini jawaban yang benar. Pasti pergi dengan 2 acara readydan turbolinks:load.
lucasarruda
5

Anda harus menggunakan:

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

dari turbolinks doc .

Gian Franco Fioriello
sumber
2

Alih-alih menggunakan variabel untuk menyimpan fungsi "siap" dan mengikatnya ke acara, Anda mungkin ingin memicu readyacara setiap kali page:loadmemicu.

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

Inilah yang telah saya lakukan untuk memastikan hal-hal tidak dieksekusi dua kali:

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

Saya menemukan menggunakan jquery-turbolinkspermata atau menggabungkan $(document).readydan $(document).on("page:load")atau menggunakan $(document).on("page:change")dengan sendirinya berperilaku tak terduga - terutama jika Anda sedang dalam pengembangan.

Gray Kemmey
sumber
Apakah Anda keberatan menjelaskan apa yang Anda maksudkan dengan bersikap tidak terduga?
sunnyrjuneja
Jika saya hanya memiliki peringatan sederhana tanpa $(document).offsedikit pun di "halaman: ubah" fungsi anonim di atas itu jelas akan mengingatkan ketika saya sampai ke halaman itu. Tetapi setidaknya dalam pengembangan menjalankan WEBrick, itu juga akan mengingatkan ketika saya mengklik tautan ke halaman lain. Kejadian yang lebih buruk, peringatan itu muncul dua kali ketika saya mengklik kembali tautan ke halaman asli.
Gray Kemmey
2

Saya menemukan artikel berikut yang bekerja sangat baik bagi saya dan merinci penggunaan berikut ini:

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

Saya pikir saya akan meninggalkan ini di sini untuk mereka yang memperbarui ke Turbolinks 5 : cara termudah untuk memperbaiki kode Anda adalah dengan mulai dari:

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

untuk:

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

Referensi: https://github.com/turbolinks/turbolinks/issues/9#issuecomment-184717346

tirdadc
sumber
2
$(document).ready(ready)  

$(document).on('turbolinks:load', ready)
Konstantinos Mou
sumber
2
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda.
andreas
4
Sebenarnya, jawaban ini salah. The turbolinks:loadperistiwa kebakaran untuk buka halaman awal serta setelah link berikut. Jika Anda melampirkan suatu acara ke readyacara standar dan turbolinks:loadacara tersebut, acara akan menyala dua kali saat Anda pertama kali mengunjungi halaman.
alexanderbird
Jawaban ini salah. Seperti yang dikatakan @alexanderbird, ini menyebabkan readyapi dua kali pada halaman berikutnya. Silakan ubah atau hapus.
chester
@alexanderbird Komentar Anda memecahkan salah satu masalah bagi saya
Anwar
1

Diuji begitu banyak solusi akhirnya sampai pada ini. Ini banyak kode Anda pasti tidak dipanggil dua kali.

      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
sumber
1

Saya biasanya melakukan hal berikut untuk proyek rel 4:

Di application.js

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

Kemudian di sisa file .js, alih-alih menggunakan $(function (){})I callonInit(function(){})

muZk
sumber
0

Pertama, instal jquery-turbolinkspermata. Dan kemudian, jangan lupa untuk memindahkan file Javascript Anda yang disertakan dari ujung tubuh Anda application.html.erbke file Javascript -nya <head>.

Seperti yang dijelaskan di sini , jika Anda telah meletakkan tautan javascript aplikasi di footer untuk alasan optimasi kecepatan, Anda harus memindahkannya ke dalam tag sehingga dimuat sebelum konten di dalam tag. Solusi ini berhasil untuk saya.

Aboozar Rajabi
sumber
0

Saya menemukan fungsi saya dua kali lipat ketika menggunakan fungsi untuk readydan turbolinks:loadjadi saya menggunakan,

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

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

Dengan begitu fungsi Anda tidak berlipat ganda jika turbolinks didukung!

Lee Eather
sumber