Rails 4: как использовать $ (document) .ready () с турбо-ссылками

422

Я столкнулся с проблемой в своем приложении на Rails 4, пытаясь организовать файлы JS «по пути». Ранее они были разбросаны по разным взглядам. Я организовал их в отдельные файлы и скомпилировал их с конвейером ресурсов. Однако я только что узнал, что событие «готово» в jQuery не срабатывает при последующих щелчках, когда включена турбо-ссылка. При первой загрузке страницы это работает. Но когда вы нажимаете на ссылку, ничего внутри ready( function($) {не будет выполнено (потому что страница на самом деле не загружается снова). Хорошее объяснение: здесь .

Поэтому мой вопрос: как правильно обеспечить правильную работу событий jQuery при включенных турбо-ссылках? Вы оборачиваете сценарии в специфичный для Rails слушатель? А может, у рельсов есть какая-то магия, которая делает это ненужным? Документы немного расплывчаты о том, как это должно работать, особенно в отношении загрузки нескольких файлов через манифест (ы), такие как application.js.

emersonthis
источник

Ответы:

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

Edit 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
К вашему сведению, вы также можете применить это к нескольким событиям page:loadи readyвключив разделенную пробелами строку для первого аргумента. $(document).on('page:load ready', ready);Второй аргумент - это просто функция с именем ready, которое следует примеру этого ответа.
ahnbizcad
235

Я только что узнал о другом варианте решения этой проблемы. Если вы загружаете на jquery-turbolinksдрагоценный камень будет связывать события Rails Turbolinks к document.readyсобытиям , так что вы можете написать JQuery обычным способом. Вы просто добавляете jquery.turbolinksсразу после этого jqueryв файл манифеста js (по умолчанию:) application.js.

emersonthis
источник
2
Круто, спасибо! Именно то, что мне нужно. Рельсы кстати. Конвенция.
Джереми Беккер
6
Согласно документации , вы должны добавить //= require jquery.turbolinksв манифест (например, application.js).
apocryphalauthor
1
@wildmonkey два ответа взаимоисключающие. 0.o
ahnbizcad
1
Вам нужно перезапустить сервер rails, когда вы добавите этот драгоценный камень
Тоби 1 Кеноби
21
Обратите внимание, что в Rails 4 теперь по умолчанию используется Turbolinks 5, который, в свою очередь, не поддерживается jquery.turbolinks! См. Github.com/kossnocorp/jquery.turbolinks/issues/56
Себастьян
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часть сделана недействительной BY, используя драгоценный камень?
Анбизкад
Самый простой и легкий способ сделать это. Он поддерживает код js, распределенный в нескольких файлах, и мне не нужно беспокоиться о том, в каком порядке они были включены. Гораздо чище, чем переменное присваивание.
Евгения Манолова
3
Вам, вероятно, следует использовать «page: change» вместо «page: load», так как первый запускается независимо от того, загружается ли страница с сервера или из кэша на стороне клиента .
Натан Лонг
91

Обнаружил это в документации по Rails 4 , аналогично решению DemoZluk, но немного короче:

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

ИЛИ

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

Если у вас есть внешние скрипты, которые вызывают $(document).ready()или если вы не можете переписывать все существующие JavaScript, этот гем позволяет вам продолжать использовать $(document).ready()с TurboLinks: https://github.com/kossnocorp/jquery.turbolinks

Migu
источник
79

Согласно новым направляющим рельсов , правильный путь заключается в следующем:

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

или, в coffeescript:

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

Не слушайте событие, $(document).readyи будет запущено только одно событие. Никаких сюрпризов, нет необходимости использовать драгоценный камень jquery.turbolinks .

Это работает с рельсами 4.2 и выше, а не только с рельсами 5.

Vedant
источник
здорово, это сработало для меня. Пробное решение здесь: stackoverflow.com/questions/17881384/… но по какой-то причине не сработало. Теперь я загружаю на турболинки: загружаю вместо
krinker
1
Кто-нибудь может подтвердить, что "turbolinks:load"событие работает в Rails 4.2? turbolinks:Приставка события была введена в Нью - Turbolinks и не используется в Rails 4.2 IIRC , который использует Turbolinks Классики . Попробуйте попробовать, "page:change"если "turbolinks:load"не работает в вашем приложении до Rails 5. См. Guides.rubyonrails.org/v4.2.7/…
Элиот Сайкс,
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 переводит события в старые имена событий: github.com/turbolinks/turbolinks/blob/v5.0.0/src/turbolinks/…
Элиот Сайкс,
1
Нужно ли alert "page has loaded!"использовать отступ для фактического запуска функцией обратного вызова?
Мбиграс
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. Хорошо сделано!
пироспад
@sled Так значит ли это использование метода SDP с использованием гема и этого кода? Или это ответ, который не использует драгоценный камень?
ahnbizcad
забудьте этот ответ и используйте решение SDP.
сани
@sled Можно ли объединить решение SDP с идеей размещения вызова только в одном месте?
ahnbizcad
1
@ Gwho: Как показывает ответ SDP, турболинки подключаются к ready события, это означает , что вы можете использовать «стандартный» способ инициализации: $(document).ready(function() { /* do your init here */});. Ваш код инициализации должен быть вызван, когда полная страница загружена (-> без TurboLinks), и ваш код инициализации должен быть выполнен снова, когда вы загружаете страницу через TurboLinks. Если вы хотите выполнить какой-то код ТОЛЬКО при использовании турболинка:$(document).on('page:load', function() { /* init only for turbolinks */});
сани
8

Ничто из вышеперечисленного не работает для меня, я решил это, не используя jQuery $ (document) .ready, а вместо этого использовал addEventListener.

document.addEventListener("turbolinks:load", function() {
  // do something
});
RockL
источник
7
$(document).on 'ready turbolinks:load', ->
  console.log '(document).turbolinks:load'
Сукерти Адига
источник
Это единственное решение, которое работает для меня в каждом браузере, для Turbolinks> = 5 и срабатывает как при загрузке первой страницы, так и при нажатии на любые ссылки (с турболинками). Это так, потому что, хотя 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');
});
wachunei
источник
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, он также будет предупреждать, когда я нажимаю ссылку на другую страницу. Событие хуже, оно предупреждает дважды, когда я затем нажимаю ссылку на исходную страницу.
Серый Кемми
2

Я нашел следующую статью, которая отлично работает для меня, и подробно описывает использование следующего:

var load_this_javascript = function() { 
  // do some things 
}
$(document).ready(load_this_javascript)
$(window).bind('page:change', load_this_javascript)
ATW
источник
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

tirdadc
источник
2
$(document).ready(ready)  

$(document).on('turbolinks:load', ready)
Константинос Мо
источник
2
Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код.
Андреас
4
На самом деле, этот ответ неверен. В turbolinks:loadсрабатывает событие для начальной загрузки страницы, а также после следующих ссылок. Если вы прикрепите событие как к стандартному readyсобытию, так и к turbolinks:loadсобытию, оно сработает дважды при первом посещении страницы.
alexanderbird
Этот ответ неверен. Как сказал @alexanderbird, readyпри последующих загрузках страницы он запускается дважды. Пожалуйста, измените или удалите его.
Chester
@alexanderbird Ваш комментарий решил одну из проблем для меня
Анвар
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

Я обычно делаю следующее для своих проектов:

В application.js

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

Затем в остальных файлах .js вместо того, чтобы использовать $(function (){})я вызываюonInit(function(){})

muZk
источник
0

Сначала установите jquery-turbolinksгем. И затем, не забудьте переместить ваши включенные файлы Javascript из конца вашего тела application.html.erbв его <head>.

Как описано здесь , если вы поместили ссылку javascript приложения в нижний колонтитул в целях оптимизации скорости, вам нужно будет переместить его в тег, чтобы он загружался до содержимого в теге. Это решение сработало для меня.

Абузар Раджаби
источник
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!

Ли Эзер
источник