Rails 4: como usar $ (document) .ready () com turbo-links

422

Eu encontrei um problema no meu aplicativo Rails 4 enquanto tentava organizar arquivos JS "da maneira dos trilhos". Eles foram anteriormente espalhados por diferentes pontos de vista. Organizei-os em arquivos separados e os compilei com o pipeline de ativos. No entanto, acabei de saber que o evento "pronto" do jQuery não é acionado nos cliques subseqüentes quando o turbo-link está ativado. A primeira vez que você carrega uma página, ela funciona. Mas quando você clica em um link, qualquer coisa dentro do ready( function($) {arquivo não é executada (porque a página não é carregada novamente). Boa explicação: aqui .

Portanto, minha pergunta é: Qual é a maneira correta de garantir que os eventos jQuery funcionem corretamente enquanto os links turbo estiverem ativados? Você agrupa os scripts em um ouvinte específico do Rails? Ou talvez os trilhos tenham alguma mágica que o torna desnecessário? Os documentos são um pouco vagos sobre como isso deve funcionar, especialmente no que diz respeito ao carregamento de vários arquivos por meio do manifesto (s) como application.js.

emersonthis
fonte

Respostas:

554

Aqui está o que eu faço ... CoffeeScript:

ready = ->

  ...your coffeescript goes here...

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

a última linha ouve o carregamento da página, que é o que os links turbo acionarão.

Editar ... adicionando versão Javascript (por solicitação):

var ready;
ready = function() {

  ...your javascript goes here...

};

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

Editar 2 ... O Rails 5 (Turbolinks 5) page:loadtorna turbolinks:load- se e será disparado na carga inicial. Para que possamos fazer o seguinte:

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

  ...your javascript goes here...

});
Meltemi
fonte
3
Isso é roteiro de café? Ainda não aprendi. Existe um js ou jQuery equivalente ao seu exemplo?
emersonthis
6
Acho que entendi: var ready = function() { ... } $(document).ready(ready) $(document).on('page:load', ready)existe alguma preocupação com os dois .ready e 'page:load'com o gatilho?
emersonthis
4
@Emerson existe um site muito útil, com um nome explícito: js2coffee.org
MrYoshiji
16
Posso confirmar que a função ready não é chamada duas vezes. Se for uma atualização difícil, apenas o readyevento será acionado. Se for uma carga de turbolinks, apenas o page:loadevento será disparado. É impossível para ambos readye page:loadser demitido na mesma página.
Christian
7
FYI Você também pode aplicar isso a vários eventos page:loade readyincluindo uma cadeia de caracteres separada por espaço para o primeiro argumet. $(document).on('page:load ready', ready);O segundo argumento é simplesmente uma função, que é nomeada ready, seguindo o exemplo desta resposta.
ahnbizcad
235

Acabei de aprender de outra opção para resolver esse problema. Se você carregar a jquery-turbolinksgema, ela vinculará os eventos Rails Turbolinks aos document.readyeventos, para que você possa escrever seu jQuery da maneira usual. Você acabou de adicionar jquery.turbolinkslogo depois jqueryno arquivo de manifesto js (por padrão application.js:).

emersonthis
fonte
2
Incrível, obrigado! Exatamente o que eu preciso. O caminho dos trilhos. Convenção.
Jeremy Becker
6
De acordo com a documentação , você deve adicionar //= require jquery.turbolinksao manifesto (por exemplo, application.js).
precisa
1
@wildmonkey as duas respostas são mutuamente exclusivas. 0.o
ahnbizcad
1
Você precisa reiniciar o servidor rails ao adicionar esta gema #
760 Toby Kenobi
21
Observe que o Rails 4 agora está padronizado para o Turbolinks 5, que, por sua vez, não é suportado pelo jquery.turbolinks! Veja github.com/kossnocorp/jquery.turbolinks/issues/56
sebastian
201

Recentemente, encontrei a maneira mais limpa e fácil de entender:

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

OU

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

EDIT
Se você delegou eventos vinculados a document, certifique-se de anexá-los fora da readyfunção, caso contrário eles serão recuperados em todos os page:loadeventos (fazendo com que as mesmas funções sejam executadas várias vezes). Por exemplo, se você tiver chamadas como esta:

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

Tire-os da readyfunção, assim:

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

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

Eventos delegados vinculados ao documentnão precisam ser vinculados ao readyevento.

Artyom Tsoy
fonte
9
Isso é muito mais simples do que a resposta aceita para criar uma ready()função separada . Votos positivos de bônus para a explicação de vincular eventos delegados fora da readyfunção.
Christian
1
Em desvantagem deste método é que $(document).on( "ready", handler ), deprecated as of jQuery 1.8. jQuery / Pronto
mfazekas
@Dezi @mfazekas Essa solução funcionaria se você estivesse usando versões anteriores do jQuery e SEM a jóia jquery-turbolinks? Ou a readyparte é invalidada usando a gema?
Ahnbizcad
A maneira mais simples e fácil de fazer isso. Ele suporta o código js distribuído em vários arquivos e não preciso me preocupar em que ordem eles foram incluídos. Muito mais limpo que a atribuição de variáveis.
Evgenia Manolova 30/03
3
Você provavelmente deve usar "page: change" em vez de "page: load", pois o primeiro é acionado, independentemente de a página ser carregada do servidor ou do cache do cliente .
Nathan Long
91

Encontrei isso na documentação do Rails 4 , semelhante à solução do DemoZluk, mas um pouco menor:

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

OU

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

Se você tiver scripts externos que chamam $(document).ready()ou se não puder se preocupar em reescrever todo o JavaScript existente, essa gema permitirá que você continue usando o $(document).ready()TurboLinks: https://github.com/kossnocorp/jquery.turbolinks

migu
fonte
79

De acordo com as novas guias de trilhos , a maneira correta é fazer o seguinte:

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

ou, em coffeescript:

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

Não ouça o evento $(document).readye apenas um evento será acionado. Sem surpresas, sem necessidade de usar a jquery.turbolinks .

Isso funciona com os trilhos 4.2 e acima, não apenas com os trilhos 5.

vedante
fonte
bom isso funcionou para mim. Tentei a solução aqui: stackoverflow.com/questions/17881384/…, mas não funcionou por algum motivo. Agora eu carregar no turbolinks: load vez
krinker
1
Alguém pode confirmar que o "turbolinks:load"evento funciona no Rails 4.2? O turbolinks:prefixo do evento foi introduzido no New Turbolinks e não é usado no Rails 4.2 IIRC, que usa o Turbolinks Classic . Considere tentar "page:change"se "turbolinks:load"não funcionar no seu aplicativo anterior ao Rails 5. Veja guias.rubyonrails.org/v4.2.7/…
Eliot Sykes
1
@EliotSykes O guia não foi atualizado. O Rails 4.2 agora usa github.com/turbolinks/turbolinks em vez de turbolinks-classic. E, de qualquer forma, isso depende do que você especificou no seu Gemfile. Espero que você vai confirmar o mesmo em breve :)
Vedant
3
Obrigado @ vedant1811. No momento em que escrevo, estou confirmando que um novo aplicativo Rails 4.2.7 usa o Turbolinks 5 (não o turbolinks-classic). Desenvolvedores que estão lendo isso - verifique seu Gemfile.lock para a versão de turbolinks usada. Se for menor que 5,0, use page:changeou atualize os turbolinks. Também encontrado isso, pode ser relevante para alguns, onde turbolinks traduz eventos para os antigos nomes de evento: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/...
Eliot Sykes
1
A alert "page has loaded!"necessidade de ser recuado para ser realmente executada pela função de retorno de chamada?
22917 mbigras
10

NOTA: Consulte a resposta do @ SDP para obter uma solução limpa e integrada

Corrigi-o da seguinte maneira:

inclua application.js antes que os outros arquivos js dependentes do aplicativo sejam incluídos, alterando a ordem de inclusão da seguinte maneira:

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

Defina uma função global que lida com a ligação em application.js

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

Agora você pode vincular coisas como:

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

// equivalent in javascript:
onLoad(function() {
  $('a.clickable').click(function() {
    alert('link clicked');
});
trenó
fonte
2
Isso parece o mais limpo, pois você só precisa adicioná-lo em um único local, nem todos os arquivos de café. Bom trabalho!
Pyrospade # 31/13
@ sled Então, isso está dizendo para usar o método da SDP de usar a gema e esse código? Ou é uma resposta que não usa a gema?
Ahnbizcad
esqueça esta resposta e use a solução da SDP.
trenó
@sled É possível combinar a solução da SDP com a noção de fazer a chamada apenas em um só lugar?
Ahnbizcad
1
@gwho: mostra resposta de Como SDP, turbolinks conecta-se ao readyevento, isso significa que você pode usar o modo "padrão" de inicialização: $(document).ready(function() { /* do your init here */});. Seu código de inicialização deve ser chamado quando a página completa é carregada (-> sem turbolinks) e seu código de inicialização deve ser executado novamente quando você carrega uma página através de turbolinks. Se você deseja executar algum código SOMENTE quando um turbolink tiver sido usado:$(document).on('page:load', function() { /* init only for turbolinks */});
sled
8

Nenhuma das opções acima funciona para mim, resolvi isso não usando $ (document) .ready do jQuery, mas use addEventListener.

document.addEventListener("turbolinks:load", function() {
  // do something
});
RockL
fonte
7
$(document).on 'ready turbolinks:load', ->
  console.log '(document).turbolinks:load'
Sukeerthi Adiga
fonte
Essa é a única solução que funciona para mim em todo e qualquer navegador, para Turbolinks> = 5 e que aciona tanto no carregamento da primeira página quanto também quando você clica em qualquer link (com turbolinks). Esse é o caso, porque enquanto gatilhos Chrome turbolinks:loadno primeiro carregamento da página, Safari não ea maneira hacky para corrigi-lo (provocando turbolinks:loadon application.js), obviamente, quebra Chrome (que dispara duas vezes com isso). Essa é a resposta correta. Definitivamente vá com os 2 eventos readye turbolinks:load.
Lucasarruda
5

Você tem que usar:

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

do turbolinks doc .

Gian Franco Fioriello
fonte
2

Em vez de usar uma variável para salvar a função "ready" e vinculá-la aos eventos, convém acionar o readyevento sempre que page:loadacionar.

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

Aqui está o que eu fiz para garantir que as coisas não sejam executadas duas vezes:

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

Acho que o uso da jquery-turbolinksgema ou combinação $(document).readye / $(document).on("page:load")ou $(document).on("page:change")por si só se comporta inesperadamente - especialmente se você estiver em desenvolvimento.

Grey Kemmey
fonte
Você se importa de explicar o que você quer dizer com se comporta inesperadamente?
sunnyrjuneja
Se eu tiver apenas um alerta simples, sem o $(document).offbit da função anônima "page: change" acima, ele obviamente alertará quando eu chegar a essa página. Mas pelo menos no desenvolvimento executando o WEBrick, ele também alertará quando eu clicar em um link para outra página. Pior ainda, ele alerta duas vezes quando eu clico em um link para a página original.
Gray Kemmey
2

Encontrei o seguinte artigo que funcionou muito bem para mim e detalha o uso do seguinte:

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

Imaginei que deixaria isso aqui para os que estão atualizando para o Turbolinks 5 : a maneira mais fácil de corrigir seu código é:

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

para:

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

Referência: https://github.com/turbolinks/turbolinks/issues/9#issuecomment-184717346

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

$(document).on('turbolinks:load', ready)
Konstantinos Mou
fonte
2
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código.
andreas
4
Na verdade, esta resposta está incorreta. O turbolinks:loadevento é acionado para o carregamento inicial da página, bem como após os links a seguir. Se você anexar um evento ao readyevento padrão e ao turbolinks:loadevento, ele será acionado duas vezes quando você visitar uma página pela primeira vez.
Alexanderbird #
Esta resposta está incorreta. Como o @alexanderbird disse, ele readydispara duas vezes em carregamentos de páginas subsequentes. Altere ou remova-o.
Chester
@alexanderbird O seu comentário resolvido um dos problemas para mim
Anwar
1

Testado tantas soluções finalmente chegou a isso. Muitos desses códigos definitivamente não são chamados duas vezes.

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

Eu costumo fazer o seguinte nos meus 4 projetos rails:

No application.js

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

No restante dos arquivos .js, em vez de usar $(function (){})eu chamoonInit(function(){})

muZk
fonte
0

Primeiro, instale a jquery-turbolinksgema. E não esqueça de mover os arquivos Javascript incluídos do final do seu corpo application.html.erbpara o dele <head>.

Conforme descrito aqui , se você tiver colocado o link javascript do aplicativo no rodapé por motivos de otimização de velocidade, será necessário movê-lo para a tag para que seja carregado antes do conteúdo da tag. Esta solução funcionou para mim.

Aboozar Rajabi
fonte
0

Descobri minhas funções dobradas ao usar uma função para readye, turbolinks:loadpor isso, usei,

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

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

Dessa forma, suas funções não dobram se turbolinks forem suportados!

Lee Eather
fonte