Rails 4: cách sử dụng $ (tài liệu). Đã () với các liên kết turbo


422

Tôi gặp phải một vấn đề trong ứng dụng Rails 4 của mình trong khi cố gắng tổ chức các tệp JS "cách rails". Chúng trước đây nằm rải rác trên các quan điểm khác nhau. Tôi đã sắp xếp chúng thành các tệp riêng biệt và biên dịch chúng với đường dẫn tài sản. Tuy nhiên, tôi mới biết rằng sự kiện "sẵn sàng" của jQuery không kích hoạt các lần nhấp tiếp theo khi bật liên kết turbo. Lần đầu tiên bạn tải một trang nó hoạt động. Nhưng khi bạn nhấp vào một liên kết, mọi thứ bên trong ready( function($) {sẽ không được thực thi (vì trang không thực sự tải lại). Giải thích tốt: ở đây .

Vì vậy, câu hỏi của tôi là: cách đúng đắn để đảm bảo các sự kiện jQuery hoạt động chính xác trong khi các liên kết turbo được bật là gì? Bạn có bao bọc các tập lệnh trong một trình nghe dành riêng cho Rails không? Hoặc có thể đường ray có một số phép thuật làm cho nó không cần thiết? Các tài liệu hơi mơ hồ về cách thức hoạt động của tính năng này, đặc biệt là đối với việc tải nhiều tệp thông qua (các) tệp kê khai như application.js.

Câu trả lời:


554

Đây là những gì tôi làm ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

dòng cuối cùng lắng nghe tải trang, đó là những gì liên kết turbo sẽ kích hoạt.

Chỉnh sửa ... thêm phiên bản Javascript (theo yêu cầu):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Chỉnh sửa 2 ... Đối với Rails 5 (Turbolinks 5) page:loadtrở thành turbolinks:loadvà thậm chí sẽ được bắn khi tải ban đầu. Vì vậy, chúng ta có thể làm như sau:

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

  ...your javascript goes here...

});

3
Đó có phải là kịch bản cà phê? Tôi chưa học được nó. Có js hoặc jQuery tương đương với ví dụ của bạn không?
emersonthis

6
Nghĩ rằng tôi đã nhận nó: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)Có bất kỳ mối quan tâm cho cả hai .ready'page:load'được kích hoạt?
emersonthis

4
@Emerson có một trang web rất hữu ích, với một cái tên rõ ràng: js2coffee.org
MrYoshiji

16
Tôi có thể xác nhận rằng hàm sẵn sàng không được gọi hai lần. Nếu đó là một refresh cứng, chỉ những readytrường hợp bị sa thải. Nếu đó là một tải turbolinks, chỉ những page:loadtrường hợp bị sa thải. Không thể cho cả hai readypage:loadbị sa thải trên cùng một trang.
Christian

7
FYI Bạn cũng có thể áp dụng điều này cho nhiều sự kiện page:loadreadybằng cách bao gồm một chuỗi được phân tách bằng dấu cách cho đối số đầu tiên. $(document).on('page:load ready', ready);Đối số thứ hai chỉ đơn giản là một hàm, được đặt tên ready, theo ví dụ của câu trả lời này.
ahnbizcad

235

Tôi vừa học được một lựa chọn khác để giải quyết vấn đề này. Nếu bạn tải các jquery-turbolinksviên ngọc đó sẽ ràng buộc các sự kiện Rails Turbolinks đến document.readycác sự kiện, do đó bạn có thể viết jQuery của bạn theo cách thông thường. Bạn chỉ cần thêm jquery.turbolinksngay sau jquerytrong tệp kê khai js (theo mặc định application.js:).


2
Thật sự cảm ơn! Chính xác những gì tôi cần. Đường ray. Quy ước.
Jeremy Becker

6
Theo tài liệu hướng dẫn , bạn nên thêm //= require jquery.turbolinksvào tệp kê khai (ví dụ application.js).
apocryphalauthor

1
@wildmonkey hai câu trả lời là loại trừ lẫn nhau. 0.o
ahnbizcad

1
Bạn cần khởi động lại máy chủ rails của mình khi thêm viên ngọc này
Toby 1 Kenobi

21
Xin lưu ý rằng Rails 4 hiện đang được mặc định là Turbolinks 5, do đó, không được jquery.turbolinks hỗ trợ! Xem github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian

201

Gần đây tôi thấy cách xử lý sạch sẽ và dễ hiểu nhất:

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

HOẶC LÀ

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

EDIT
Nếu bạn đã ủy thác các sự kiện được liên kết với document, hãy đảm bảo bạn đính kèm chúng bên ngoài readychức năng, nếu không chúng sẽ phục hồi trên mọi page:loadsự kiện (khiến các chức năng tương tự được chạy nhiều lần). Ví dụ: nếu bạn có bất kỳ cuộc gọi nào như thế này:

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

Đưa họ ra khỏi readychức năng, như thế này:

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

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

Các sự kiện được ủy quyền ràng buộc documentkhông cần phải bị ràng buộc vào readysự kiện.


9
Điều này đơn giản hơn nhiều so với câu trả lời được chấp nhận của việc tạo một ready()hàm riêng biệt . Phần thưởng nâng cao cho việc giải thích các sự kiện được ủy nhiệm ràng buộc bên ngoài readychức năng.
Christian

1
Bất lợi của phương pháp này là $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jquery / ready
mfazekas

@Dezi @mfazekas Giải pháp này có hiệu quả không nếu bạn đang sử dụng các phiên bản jQuery trước đó và KHÔNG CÓ đá quý jquery-turbolinks? Hoặc là readyphần được làm không hợp lệ bằng cách sử dụng đá quý?
ahnbizcad

Cách đơn giản nhất và dễ nhất để làm điều đó. Nó hỗ trợ mã js được phân phối trong nhiều tệp và tôi không phải bận tâm đến thứ tự chúng được bao gồm. Sạch sẽ hơn nhiều so với việc gán biến.
Evgenia Manolova 30/03/2015


91

Tìm thấy điều này trong tài liệu Rails 4 , tương tự như giải pháp của DemoZluk nhưng ngắn hơn một chút:

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

HOẶC LÀ

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

Nếu bạn có các tập lệnh bên ngoài gọi $(document).ready()hoặc nếu bạn không thể bận tâm viết lại tất cả JavaScript hiện có của mình, thì viên ngọc này cho phép bạn tiếp tục sử dụng $(document).ready()với TurboLinks: https://github.com/kossnocorp/jquery.turbolinks


79

Theo hướng dẫn đường ray mới , cách chính xác là làm như sau:

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

hoặc, trong cà phê:

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

Đừng nghe sự kiện $(document).readyvà chỉ có một sự kiện sẽ bị sa thải. Không có gì ngạc nhiên, không cần sử dụng đá quý jquery.turbolinks .

Điều này hoạt động với đường ray 4.2 trở lên, không chỉ đường ray 5.


tốt đẹp này làm việc cho tôi. Đã thử giải pháp tại đây: stackoverflow.com/questions/17881384/ trên nhưng không hoạt động vì một số lý do. Bây giờ tôi tải trên turbolinks: tải thay thế
krinker

1
Bất cứ ai cũng có thể xác nhận rằng "turbolinks:load"sự kiện này hoạt động trong Rails 4.2? Các turbolinks:tiền tố Sự kiện này được giới thiệu trong New Turbolinks và không được sử dụng trong Rails 4.2 IIRC trong đó sử dụng Turbolinks cổ điển . Cân nhắc thử "page:change"nếu "turbolinks:load"không hoạt động trên ứng dụng Rails 5 của bạn. Xem hướng dẫn.rubyonrails.org / v4.2.7 / Mạnh
Eliot Sykes

1
@EliotSykes Hướng dẫn chưa được cập nhật. Rails 4.2 hiện sử dụng github.com/turbolinks/turbolinks thay vì turbolinks-classic. Và trong mọi trường hợp, điều này phụ thuộc vào những gì bạn đã chỉ định trong Gemfile của mình. Hy vọng bạn sẽ sớm xác nhận điều tương tự :)
vedant

3
Cảm ơn @ vedant1811. Tại thời điểm viết bài, tôi xác nhận rằng ứng dụng Rails 4.2.7 mới sử dụng Turbolinks 5 (không phải turbolinks-classic). Các nhà phát triển đọc nó - kiểm tra Gemfile.lock của bạn để biết phiên bản turbolinks được sử dụng. Nếu nó nhỏ hơn 5.0 thì hãy sử dụng page:changehoặc nâng cấp turbolinks. Cũng tìm thấy điều này, có thể có liên quan đến một số người, trong đó turbolinks dịch các sự kiện sang tên sự kiện cũ: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/,
Eliot Sykes

1
alert "page has loaded!"cần phải được thụt lề để thực sự được chạy bởi chức năng gọi lại?
mbigras

10

LƯU Ý: Xem câu trả lời của @ SDP để biết giải pháp tích hợp sẵn

Tôi đã sửa nó như sau:

đảm bảo bạn bao gồm application.js trước khi các tệp js phụ thuộc ứng dụng khác được bao gồm bằng cách thay đổi thứ tự bao gồm như sau:

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

Xác định hàm toàn cục xử lý ràng buộc trong application.js

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

Bây giờ bạn có thể liên kết những thứ như:

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

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

2
Điều này có vẻ sạch nhất vì bạn chỉ phải thêm cái này ở một nơi, không phải mọi tệp coffeescript. Công việc tốt đẹp!
pyrospade

@sled Vậy có phải câu nói này sử dụng phương pháp sử dụng đá quý và mã này của SDP không? Hay đây là một câu trả lời không sử dụng đá quý?
ahnbizcad

quên câu trả lời này và sử dụng giải pháp của SDP.
kéo dài

@sled Có thể kết hợp giải pháp của SDP với khái niệm chỉ thực hiện cuộc gọi ở một nơi không?
ahnbizcad

1
@gwho: Như câu trả lời của SDP cho thấy, turbolinks liên kết với readysự kiện, điều này có nghĩa là bạn có thể sử dụng cách khởi tạo "tiêu chuẩn" : $(document).ready(function() { /* do your init here */});. Mã init của bạn sẽ được gọi khi trang hoàn chỉnh được tải (-> không có turbolinks) và mã init của bạn sẽ được thực thi lại khi bạn tải một trang qua turbolinks. Nếu bạn muốn thực thi một số mã CHỈ khi một turbolink đã được sử dụng:$(document).on('page:load', function() { /* init only for turbolinks */});
kéo dài

8

Không có cách nào ở trên hoạt động với tôi, tôi đã giải quyết điều này bằng cách không sử dụng $ (tài liệu) của jQuery. Thay vào đó, nhưng sử dụng addEventListener.

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

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

Đó là giải pháp duy nhất phù hợp với tôi trên mọi trình duyệt, cho Turbolinks> = 5 và kích hoạt cả khi tải trang đầu tiên và cả khi bạn nhấp vào bất kỳ liên kết nào (với turbolinks). Đó là trường hợp bởi vì trong khi Chrome kích hoạt turbolinks:loadở lần tải trang đầu tiên, Safari không và cách khắc phục sự cố (kích hoạt turbolinks:loadtrên application.js) rõ ràng đã phá vỡ Chrome (điều này kích hoạt hai lần với điều này). Đây là câu trả lời chính xác. Chắc chắn đi với 2 sự kiện readyturbolinks:load.
lucasarruda



2

Thay vì sử dụng một biến để lưu chức năng "sẵn sàng" và liên kết nó với các sự kiện, bạn có thể muốn kích hoạt readysự kiện bất cứ khi nào page:loadkích hoạt.

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

2

Đây là những gì tôi đã làm để đảm bảo mọi thứ không được thực hiện hai lần:

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

Tôi thấy việc sử dụng jquery-turbolinksđá quý hoặc kết hợp $(document).ready$(document).on("page:load")hoặc sử dụng $(document).on("page:change")chính nó hoạt động bất ngờ - đặc biệt là nếu bạn đang trong quá trình phát triển.


Bạn có phiền giải thích những gì bạn có nghĩa là bằng cách cư xử bất ngờ?
sunnyrjuneja

Nếu tôi chỉ có một cảnh báo đơn giản mà không có $(document).offbit trong chức năng "trang: thay đổi" ẩn danh đó thì rõ ràng nó sẽ cảnh báo khi tôi đến trang đó. Nhưng ít nhất trong quá trình phát triển chạy WEBrick, nó cũng sẽ cảnh báo khi tôi nhấp vào một liên kết đến một trang khác. Sự kiện tồi tệ hơn, nó thông báo hai lần khi tôi nhấp vào liên kết trở lại trang gốc.
Gray Kemmey

2

Tôi tìm thấy bài viết sau đây rất hữu ích cho tôi và chi tiết việc sử dụng như sau:

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
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
andreas

4
Trên thực tế, câu trả lời này là không chính xác. Sự turbolinks:loadkiện kích hoạt tải trang ban đầu cũng như sau các liên kết sau. Nếu bạn đính kèm một sự kiện cả vào readysự kiện tiêu chuẩn và turbolinks:loadsự kiện đó, nó sẽ kích hoạt hai lần khi bạn truy cập trang đầu tiên.
alexanderbird

Câu trả lời này không chính xác. Như @alexanderbird đã nói, nó gây ra readybắn hai lần vào các lần tải trang tiếp theo. Vui lòng thay đổi hoặc loại bỏ nó.
chester

@alexanderbird Nhận xét của bạn đã giải quyết một trong những vấn đề cho tôi
Anwar

1

Đã thử nghiệm rất nhiều giải pháp cuối cùng đã đến. Điều này nhiều mã của bạn chắc chắn không được gọi hai lần.

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

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


1

Tôi thường làm như sau cho 4 dự án đường ray của tôi:

Trong application.js

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

Sau đó, trong phần còn lại của tệp .js, thay vì sử dụng $(function (){})tôi gọionInit(function(){})


0

Đầu tiên, cài đặt jquery-turbolinksđá quý. Và sau đó, đừng quên chuyển các tệp Javascript được bao gồm của bạn từ phần cuối của cơ thể application.html.erbsang tập tin của bạn <head>.

Như được mô tả ở đây , nếu bạn đã đặt liên kết javascript của ứng dụng vào chân trang vì lý do tối ưu hóa tốc độ, bạn sẽ cần chuyển nó vào thẻ để nó tải trước nội dung trong thẻ. Giải pháp này đã làm việc cho tôi.


0

Tôi thấy các chức năng của mình tăng gấp đôi khi sử dụng một chức năng readyturbolinks:loadvì vậy tôi đã sử dụng,

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

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

Bằng cách đó, chức năng của bạn sẽ không tăng gấp đôi nếu turbolinks được hỗ trợ!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.