Rails 4: วิธีใช้ $ (เอกสาร) .ready () พร้อมลิงค์เทอร์โบ


422

ฉันพบปัญหาในแอป Rails 4 ของฉันในขณะที่พยายามจัดระเบียบไฟล์ JS "ทางรถไฟ" ก่อนหน้านี้พวกเขากระจัดกระจายในมุมมองที่แตกต่างกัน ฉันจัดระเบียบพวกเขาเป็นไฟล์แยกต่างหากและรวบรวมพวกเขาด้วยท่อสินทรัพย์ อย่างไรก็ตามฉันเพิ่งรู้ว่าเหตุการณ์ "พร้อม" ของ jQuery ไม่ได้เกิดจากการคลิกครั้งต่อไปเมื่อเปิดการเชื่อมโยงแบบเทอร์โบ ครั้งแรกที่คุณโหลดหน้าเว็บ แต่เมื่อคุณคลิกลิงก์สิ่งใดก็ตามที่อยู่ภายในready( function($) {จะไม่ถูกดำเนินการ (เนื่องจากหน้าเว็บไม่ได้โหลดอีกครั้ง) คำอธิบายที่ดี: ที่นี่

ดังนั้นคำถามของฉันคืออะไรวิธีที่เหมาะสมเพื่อให้แน่ใจว่าเหตุการณ์ jQuery ทำงานอย่างถูกต้องในขณะที่ลิงค์เทอร์โบเปิดอยู่ คุณใส่สคริปต์ในตัวฟังเฉพาะของ Rails หรือไม่? หรือราวอาจมีเวทมนตร์ที่ทำให้มันไม่จำเป็น? เอกสารนั้นค่อนข้างคลุมเครือเกี่ยวกับวิธีการใช้งานโดยเฉพาะอย่างยิ่งการโหลดไฟล์หลายไฟล์ผ่านไฟล์ Manifest เช่น application.js

คำตอบ:


554

นี่คือสิ่งที่ฉันทำ ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

บรรทัดสุดท้ายรับฟังการโหลดหน้าซึ่งเป็นสิ่งที่ลิงค์เทอร์โบจะทริกเกอร์

แก้ไข ... เพิ่มรุ่น Javascript (ต่อคำขอ):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

แก้ไข 2 ... สำหรับ Rails 5 (Turbolinks 5) page:loadกลายเป็นturbolinks:loadและจะถูกยิงแม้ในการโหลดครั้งแรก ดังนั้นเราสามารถทำสิ่งต่อไปนี้:

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

  ...your javascript goes here...

});

3
สคริปต์กาแฟนั้นใช่ไหม ฉันยังไม่ได้เรียนเลย มี js หรือ jQuery เทียบเท่ากับตัวอย่างของคุณหรือไม่?
emersonthis

6
คิดว่าฉันเข้าใจแล้ว: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)มีข้อกังวลใด ๆ สำหรับทั้งคู่ .readyและ'page:load'ถูกกระตุ้น?
emersonthis

4
@Emerson มีเว็บไซต์ที่มีประโยชน์มากโดยมีชื่อชัดเจน: js2coffee.org
MrYoshiji

16
ฉันสามารถยืนยันได้ว่าฟังก์ชั่นเตรียมพร้อมไม่ถูกเรียกสองครั้ง ถ้ามันเป็นรีเฟรชหนักเพียงreadyเหตุการณ์ถูกยิง ถ้ามันเป็นภาระ turbolinks, เพียงpage:loadเหตุการณ์ถูกยิง เป็นไปไม่ได้สำหรับทั้งคู่readyและpage:loadถูกไล่ออกในหน้าเดียวกัน
คริสเตียน

7
FYI คุณยังสามารถใช้สิ่งนี้กับหลาย ๆ เหตุการณ์page:loadและreadyโดยการรวมสตริงที่คั่นด้วยช่องว่างสำหรับอาร์กิวเมนต์แรก $(document).on('page:load ready', ready);อาร์กิวเมนต์ที่สองเป็นเพียงฟังก์ชั่นซึ่งเกิดขึ้นกับชื่อreadyตามตัวอย่างของคำตอบนี้
ahnbizcad

235

ฉันเพิ่งเรียนรู้วิธีการอื่นในการแก้ปัญหานี้ ถ้าคุณโหลดอัญมณีก็จะผูกเหตุการณ์ Rails Turbolinks กับเหตุการณ์ที่เกิดขึ้นเพื่อให้คุณสามารถเขียน jQuery ของคุณในทางปกติ คุณเพียงแค่เพิ่มทันทีหลังจากที่ใน js ไฟล์ Manifest (โดยค่าเริ่มต้น: )jquery-turbolinksdocument.readyjquery.turbolinksjqueryapplication.js


2
เยี่ยมมากขอบคุณ! สิ่งที่ฉันต้องการ ทางรถไฟ การประชุม
Jeremy Becker

6
ตามเอกสารคุณควรเพิ่มลง//= require jquery.turbolinksในรายการ (เช่น application.js)
apocryphalauthor

1
@wildmonkey คำตอบสองข้อนั้นไม่เหมือนกัน 0.o
ahnbizcad

1
คุณต้องรีสตาร์ทเซิร์ฟเวอร์ rails ของคุณเมื่อคุณเพิ่ม gem นี้
Toby 1 Kenobi

21
โปรดทราบว่า Rails 4 เริ่มต้นเป็น Turbolinks 5 ซึ่ง jquery.turbolinks ไม่สนับสนุน! ดูgithub.com/kossnocorp/jquery.turbolinks/issues/56
sebastian

201

เมื่อเร็ว ๆ นี้ฉันพบวิธีจัดการกับวิธีที่สะอาดและเข้าใจง่ายที่สุด:

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

หรือ

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

แก้ไข
หากคุณได้มอบหมายกิจกรรมที่ถูกผูกไว้กับdocumentให้แน่ใจว่าคุณแนบไว้นอกreadyฟังก์ชั่นมิฉะนั้นพวกเขาจะได้รับการตอบสนองในทุกpage:loadเหตุการณ์ (ทำให้ฟังก์ชั่นเดียวกันทำงานหลายครั้ง) ตัวอย่างเช่นหากคุณมีสายเช่นนี้:

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

นำพวกเขาออกจากreadyฟังก์ชั่นเช่นนี้:

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

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

เหตุการณ์ที่ได้รับมอบหมายผูกพันกับ documentไม่จำเป็นต้องถูกผูกไว้กับreadyเหตุการณ์


9
นี่เป็นคำตอบที่ง่ายกว่าการสร้างready()ฟังก์ชั่นแยกต่างหาก โบนัสอัปโหลดสำหรับคำอธิบายของเหตุการณ์ที่มอบหมายที่มีผลผูกพันนอกreadyฟังก์ชัน
คริสเตียน

1
ข้อเสียของวิธีนี้คือ$(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas

@Dezi @mfazekas โซลูชันนี้จะใช้งานได้หรือไม่หากคุณใช้ jQuery รุ่นก่อนหน้าและไม่มีอัญมณี jquery-turbolinks หรือreadyส่วนนั้นทำให้การใช้อัญมณีเป็นโมฆะ?
ahnbizcad

วิธีที่ง่ายที่สุดและง่ายที่สุดที่จะทำ รองรับรหัส js ที่แจกจ่ายในหลายไฟล์และฉันไม่ต้องกังวลว่าจะเรียงลำดับอย่างไร สะอาดกว่าการกำหนดตัวแปรมาก
Evgenia Manolova

3
คุณอาจจะใช้คำว่า "หน้านี้: การเปลี่ยนแปลง" แทน "หน้านี้: โหลด" ขณะที่อดีตเป็นเชื้อเพลิงว่าหน้าถูกโหลดจากเซิร์ฟเวอร์หรือจากแคชฝั่งไคลเอ็นต์
นาธานลอง

91

พบสิ่งนี้ในเอกสารประกอบ Rails 4ซึ่งคล้ายกับโซลูชันของ DemoZluk แต่สั้นกว่าเล็กน้อย:

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

หรือ

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

หากคุณมีสคริปต์ภายนอกที่โทรออก$(document).ready()หรือไม่สามารถเขียนจาวาสคริปต์ที่มีอยู่ทั้งหมดของคุณใหม่ได้อัญมณีนี้จะช่วยให้คุณสามารถใช้งาน$(document).ready()กับ TurboLinks: https://github.com/kossnocorp/jquery.turbolinks


79

ตามคู่มือรางใหม่วิธีที่ถูกต้องคือทำดังต่อไปนี้:

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

หรือในกาแฟ:

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

อย่าฟังเหตุการณ์$(document).readyและมีเหตุการณ์เดียวเท่านั้นที่จะถูกไล่ออก ไม่น่าประหลาดใจไม่จำเป็นต้องใช้อัญมณีjquery.turbolinks

ใช้งานได้กับราง 4.2 ขึ้นไปไม่เพียง แต่ราง 5


ดีนี้ทำงานได้สำหรับฉัน พยายามแก้ปัญหาที่นี่: stackoverflow.com/questions/17881384/…แต่ไม่ได้ทำงานด้วยเหตุผลบางอย่าง ตอนนี้ฉันโหลด turbolinks: load แทน
krinker

1
ทุกคนสามารถยืนยันได้ว่า"turbolinks:load"เหตุการณ์ทำงานใน Rails 4.2 ได้หรือไม่ turbolinks:คำนำหน้าเหตุการณ์ที่ได้รับการแนะนำในNew Turbolinks และไม่ได้นำมาใช้ในทางรถไฟ 4.2 IIRC ซึ่งใช้ Turbolinks คลาสสิก ลองทำดู"page:change"ถ้า"turbolinks:load"ไม่ได้ทำงานกับแอพ pre-Rails 5 ของคุณ ดูguide.rubyonrails.org/v4.2.7/…
Eliot Sykes

1
@EliotSykes คู่มือยังไม่ได้รับการปรับปรุง Rails 4.2 ใช้github.com/turbolinks/turbolinksแทน turbolinks-classic และในกรณีใด ๆ สิ่งนี้ขึ้นอยู่กับสิ่งที่คุณระบุใน Gemfile ของคุณ หวังว่าคุณจะยืนยันเร็ว ๆ นี้เหมือนกัน :)
vedant

3
ขอบคุณ @ vedant1811 ในขณะที่เขียนฉันยืนยันว่าแอพ Rails 4.2.7 ใหม่ใช้ Turbolinks 5 (ไม่ใช่ turbolinks-classic) นักพัฒนาที่อ่านสิ่งนี้ - ตรวจสอบ Gemfile.lock ของคุณเพื่อดูรุ่น turbolinks ที่ใช้ ถ้ามันน้อยกว่า 5.0 ให้ใช้page:changeหรืออัพเกรด turbolinks นอกจากนี้ยังพบนี้อาจจะเกี่ยวข้องกับบางอย่างที่ turbolinks แปลกิจกรรมเพื่อชื่อเหตุการณ์อายุ: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/...
เอเลียต Sykes

1
ที่ไม่alert "page has loaded!"จำเป็นต้องได้รับการเยื้องที่จะทำงานจริงโดยฟังก์ชั่นการโทรกลับหรือไม่
mbigras

10

หมายเหตุ: ดูคำตอบของ @ SDP สำหรับโซลูชันที่สะอาดและมีอยู่ภายใน

ฉันแก้ไขมันดังนี้

ตรวจสอบให้แน่ใจว่าคุณได้รวม application.js ก่อนที่จะรวมไฟล์ js ที่ขึ้นกับแอพอื่นด้วยการเปลี่ยนลำดับการรวมดังนี้:

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

กำหนดฟังก์ชั่นระดับโลกที่จัดการผูกพัน application.js

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

ตอนนี้คุณสามารถผูกสิ่งต่าง ๆ เช่น:

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

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

2
ดูเหมือนว่าจะสะอาดที่สุดเพราะคุณต้องเพิ่มสิ่งนี้ในที่เดียวไม่ใช่ไฟล์ coffeescript ทุกไฟล์ เยี่ยมมาก!
pyrospade

@sled ดังนั้นนี่คือคำสั่งให้ใช้วิธีการของ SDP ในการใช้อัญมณีและรหัสนี้หรือไม่? หรือนี่เป็นคำตอบที่ไม่ได้ใช้อัญมณีหรือ
ahnbizcad

ลืมคำตอบนี้และใช้โซลูชันของ SDP
เลื่อน

@sled เป็นไปได้หรือไม่ที่จะรวมโซลูชันของ SDP เข้ากับแนวคิดของการวางสายในที่เดียวเท่านั้น?
ahnbizcad

1
@gwho: เป็นคำตอบของ SDP แสดง turbolinks hooks ขึ้นอยู่กับreadyเหตุการณ์นี้หมายความว่าคุณสามารถใช้วิธี "มาตรฐาน" $(document).ready(function() { /* do your init here */});ของการเริ่มต้น: รหัสเริ่มต้นของคุณควรถูกเรียกใช้เมื่อโหลดหน้าทั้งหมด (-> โดยไม่มี turbolinks) และรหัสเริ่มต้นของคุณควรถูกเรียกใช้อีกครั้งเมื่อคุณโหลดหน้าเว็บผ่านทาง turbolinks หากคุณต้องการรันโค้ดบางอย่างเฉพาะเมื่อมีการใช้ turbolink:$(document).on('page:load', function() { /* init only for turbolinks */});
เลื่อน

8

ไม่มีข้อใดที่ได้ผลสำหรับฉันฉันแก้ไขสิ่งนี้โดยไม่ใช้ $ (เอกสาร) ของ jQuery แต่พร้อมใช้ addEventListener แทน

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

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

นั่นเป็นทางออกเดียวที่เหมาะกับฉันในทุก ๆ เบราว์เซอร์สำหรับ Turbolinks> = 5 และมันจะทริกเกอร์ทั้งในการโหลดหน้าแรกและเมื่อคุณคลิกลิงก์ใด ๆ (ด้วย turbolinks) เป็นเช่นนั้นเพราะในขณะที่ Chrome ทริกเกอร์turbolinks:loadเมื่อมีการโหลดหน้าแรก Safari ไม่ได้และวิธีที่แฮ็กในการแก้ไข (เรียกturbolinks:loadใช้application.js) จะทำให้ Chrome แตก (ซึ่งมีการยิงสองครั้ง) นี่คือคำตอบที่ถูกต้อง ไปกับ 2 กิจกรรมreadyและturbolinks:loadแน่นอน
lucasarruda



2

แทนที่จะใช้ตัวแปรเพื่อบันทึกฟังก์ชัน "พร้อม" และผูกไว้กับเหตุการณ์คุณอาจต้องการทริกเกอร์readyเหตุการณ์เมื่อใดก็ตามที่page:loadเรียก

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

2

นี่คือสิ่งที่ฉันได้ทำเพื่อให้แน่ใจว่าสิ่งต่าง ๆ จะไม่ถูกดำเนินการสองครั้ง:

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

ฉันพบว่าการใช้jquery-turbolinksอัญมณีหรือการรวม$(document).readyและ$(document).on("page:load")หรือใช้งาน$(document).on("page:change")ด้วยตัวเองทำงานโดยไม่คาดคิด - โดยเฉพาะถ้าคุณกำลังพัฒนา


คุณคิดที่จะอธิบายความหมายของพฤติกรรมโดยไม่คาดคิดหรือไม่?
sunnyrjuneja

หากฉันมีการแจ้งเตือนง่ายๆโดยไม่มี$(document).offบิตในฟังก์ชั่น "page: change" ที่ไม่ระบุชื่อข้างบนจะเป็นการแจ้งเตือนเมื่อฉันไปที่หน้านั้น แต่อย่างน้อยในการพัฒนาใช้ WEBrick มันจะแจ้งเตือนเมื่อฉันคลิกลิงก์ไปยังหน้าอื่น เหตุการณ์แย่ลงมันเตือนสองครั้งเมื่อฉันคลิกลิงค์กลับไปที่หน้าเดิม
สีเทา Kemmey

2

ฉันพบบทความต่อไปนี้ซึ่งทำงานได้ดีสำหรับฉันและรายละเอียดการใช้งานต่อไปนี้:

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

2

ฉันคิดว่าฉันจะออกจากที่นี่เพื่ออัปเกรดเป็นTurbolinks 5 : วิธีที่ง่ายที่สุดในการแก้ไขรหัสของคุณคือการไปจาก:

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);

การอ้างอิง: https://github.com/turbolinks/turbolinks/issues/9#issuecomment-184717346


2
$(document).ready(ready)  

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

2
ในขณะที่ข้อมูลโค้ดนี้อาจแก้ไขคำถามรวมถึงคำอธิบายช่วยปรับปรุงคุณภาพของโพสต์ของคุณ จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและคนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ
Andreas

4
จริงๆแล้วคำตอบนี้ไม่ถูกต้อง turbolinks:loadไฟกิจกรรมสำหรับการโหลดหน้าเริ่มต้นเช่นเดียวกับหลังการเชื่อมโยงต่อไป หากคุณแนบกิจกรรมทั้งกับreadyกิจกรรมมาตรฐานและturbolinks:loadกิจกรรมมันจะเริ่มขึ้นสองครั้งเมื่อคุณเข้าชมหน้าเว็บเป็นครั้งแรก
alexanderbird

คำตอบนี้ไม่ถูกต้อง ดังที่ @alexanderbird กล่าวว่าทำให้เกิดreadyการยิงสองครั้งเมื่อโหลดหน้าเว็บต่อมา กรุณาเปลี่ยนหรือลบมัน
เชสเตอร์

@alexanderbird ความคิดเห็นของคุณแก้ไขปัญหาอย่างหนึ่งให้ฉัน
Anwar

1

ทดสอบวิธีการแก้ปัญหามากมายจนในที่สุดก็มาถึงนี้ รหัสของคุณจำนวนมากนี้ไม่ได้ถูกเรียกสองครั้งอย่างแน่นอน

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

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


1

ฉันมักจะทำสิ่งต่อไปนี้สำหรับโครงการ 4 รางของฉัน:

ใน application.js

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

จากนั้นในไฟล์. js ที่เหลือแทนที่จะใช้$(function (){})ฉันโทรonInit(function(){})


0

ก่อนอื่นให้ติดตั้งjquery-turbolinksgem จากนั้นอย่าลืมย้ายไฟล์ Javascript ที่รวมอยู่ออกจากส่วนท้ายของเนื้อหาapplication.html.erbไปยัง<head>ไฟล์

ตามที่อธิบายไว้ที่นี่หากคุณใส่ลิงก์จาวาสคริปต์ลงในส่วนท้ายเพื่อเหตุผลในการเพิ่มประสิทธิภาพความเร็วคุณจะต้องย้ายไปไว้ที่แท็กเพื่อให้โหลดได้ก่อนเนื้อหาในแท็ก วิธีนี้ใช้ได้ผลสำหรับฉัน


0

ฉันพบว่าฟังก์ชั่นของฉันเพิ่มเป็นสองเท่าเมื่อใช้ฟังก์ชั่นสำหรับreadyและturbolinks:loadดังนั้นฉันจึงใช้

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

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

วิธีนี้จะทำให้ฟังก์ชั่นของคุณไม่เพิ่มเป็นสองเท่าหากรองรับ turbolinks

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.