AdamNYC
AdamNYC

Reputation: 20435

Making jQuery works with Turbolinks

I have a Rails 4 app, which uses Turbolinks. My understanding is that Turbolinks breaks jQuery code, as Turbolinks does not load new page, but only get new elements.

Therefore, navigating to new page may not trigger .ready, although it always triggers .page:load, and thus new jQuery code won't initialize.

I have a lot of jQuery code, so I don't want to modify my jQuery code to be compatible with Turbolinks.

Is it possible to add a javascript code to my application.js that overwrites .ready event to include page:load as well? How should I do it?

Upvotes: 5

Views: 6513

Answers (5)

Kiran Maniya
Kiran Maniya

Reputation: 9009

TLDR; Here is how the conventional approach works

$(document).ready(function() {
    $("#tbl-account").tableSorter();
});

It uses jQuery to initialize a table-sorting plugin once the document finishes loading. One noticeable thing here is not teardown and re-run of this js when page component switches by Turbolink. There isn't any. There didn't need to be back in the day because the browser handled the cleanup. However, in a single-page application like Turbolinks, the browser doesn't handle it. You, the developer, have to manage the initialization and cleanup of your JavaScript behaviors.

When people try to port traditional web apps to Turbolinks, they often run into problems because their JS never cleans up after itself.

All Turbolinks-friendly JavaScript needs to:

  1. Initialize itself when a page is displayed
  2. Clean up after itself before Turbolinks navigates to a new page.

Capturing Events

Turbolinks provides its own events that you can capture to set up and tear down your JavaScript. Let's start with the tear-down: ```js document.addEventListener('turbolinks:before-render', () => { Components.unloadAll(); }); ``` The `turbolinks:before-render` event fires before each pageview except the very first one. That's perfect because on the first pageview there's nothing to tear down.

The events for initialization are a little more complicated. We want our event handler to runs:

  1. On the initial page load
  2. On any subsequent visit to a new page Here's how we capture those events:
// Called once after the initial page has loaded
document.addEventListener(
  'turbolinks:load',
  () => Components.loadAll(),
  {
    once: true,
  },
);

// Called after every non-initial page load
document.addEventListener('turbolinks:render', () =>
    Components.loadAll(),
);

Thanks to Starr Horne for writing the article on migrating from jquery/pjax to turbolinks

Upvotes: 0

wasd-wasd
wasd-wasd

Reputation: 23

i had to use the page:change event:

js:

$(document).on('page:change', function () {
    <code here>
});

coffee script:

$(document).on 'page:change' ->
    <code here>

Upvotes: 2

aldefouw
aldefouw

Reputation: 533

With TurboLinks 5 / Rails 5 ... I would recommend instantiating DataTables like this.

It will prevent the heading and footer paging from showing up multiple times when the back button is used.

$(document).on 'turbolinks:load', ->
  tableElementIds = [
    '### TABLE ID HERE ###'
  ]
  i = 0
  while i < tableElementIds.length
    tableElementId = tableElementIds[i]
    if $.isEmptyObject($.find(tableElementId))
      i++
      continue
    table = undefined
    if $.fn.DataTable.isDataTable(tableElementId)
      table = $(tableElementId).DataTable()
    else
      table = $(tableElementId).DataTable(### OPTIONS HERE ###)

    document.addEventListener 'turbolinks:before-cache', ->
      table.destroy()
      return

    i++

  return

Upvotes: 1

koppor
koppor

Reputation: 20531

With turbolinks 5.0.0, the events changed to turbolinks:load. See full list of turbolinks events.

The documentation recommends following code:

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

The jquery.turbolinks fork located at https://github.com/dalpo/jquery.turbolinks already reflects these changes and allows for a seamless drop-in of turbolinks. Nevertheless, I would go for the turbolinks:load event to have full control and not require another library.

Upvotes: 4

Tyler
Tyler

Reputation: 11499

Rather than wait for $(document).ready to fire for your jQuery, just use page:load instead:

$(document).on 'page:load' ->
  <your code>

Alternatively, you can set up the jquery.turbolinks gem: https://github.com/kossnocorp/jquery.turbolinks

Upvotes: 3

Related Questions