Search code examples
javascriptruby-on-railsturbolinksruby-on-rails-6flickity

How do I initialize Flickity with HTML in Turbolinks


I am using the Flickity library.

This is my HTML:

<section class="controls-inside controls-light p-0" data-flickity='{ "imagesLoaded": true, "wrapAround": true }'>

It works well on the first load, which creates this HTML:

<section class="controls-inside controls-light p-0 flickity-enabled is-draggable" data-flickity="{ "imagesLoaded": true, "wrapAround": true }" tabindex="0">

But once I navigate to any other page and try to go back to the homepage (where this code is), it generates just the first code at the top (i.e. the uninitialized version).

This is currently my TL code:

$(document).on("turbolinks:load", () => {
  $('[data-toggle="tooltip"]').tooltip()
  $('[data-toggle="popover"]').popover()
})

So given that I am using HTML initialization, how do I do handle this with TL?

Edit 1

I am also open to initializing Flickity via JS if it hasn't been initialized via HTML. Either way is fine by me so it works.

Edit 2

In my application.js, I now have the following:

$(document).on("turbolinks:load", () => {
  var Flickity = require('flickity');
  var flkty = new Flickity( 'section[data-flickity]', {
      imagesLoaded: true,
      wrapAround: true,
  });
})

This kinda works. What happens is when I try to navigate to another page and go back to the one that has the slider, I can see that it loads twice. As in, it loads the slider initially and I see it then midway during loading bar progress the entire page refreshes again. So it feels like it is doing twice the work. I just want it loaded once.

It also produces the following errors in my Browser Console.

On 1st load of the page:

flickity.js:57 Uncaught TypeError: Cannot read property 'option' of undefined
    at new Flickity (flickity.js:57)
    at HTMLDocument.<anonymous> (application.js:43)
    at HTMLDocument.dispatch (jquery.js:4670)
    at HTMLDocument.elemData.handle (jquery.js:4490)
    at Object../node_modules/turbolinks/dist/turbolinks.js.e.dispatch (turbolinks.js:75)
    at r.notifyApplicationAfterPageLoad (turbolinks.js:994)
    at r.pageLoaded (turbolinks.js:948)
    at turbolinks.js:872
Flickity @ flickity.js:57
(anonymous) @ application.js:43
dispatch @ jquery.js:4670
elemData.handle @ jquery.js:4490
./node_modules/turbolinks/dist/turbolinks.js.e.dispatch @ turbolinks.js:75
r.notifyApplicationAfterPageLoad @ turbolinks.js:994
r.pageLoaded @ turbolinks.js:948
(anonymous) @ turbolinks.js:872
flickity.js:57 Uncaught TypeError: Cannot read property 'option' of undefined
    at new Flickity (flickity.js:57)
    at utils.js:217
    at Array.forEach (<anonymous>)
    at utils.js:201

If I clear the errors from my console, navigate away and come back, this is the only error in my console on that second page load:

flickity.js:47 Bad element for Flickity: section[data-flickity]

What could be causing these errors and how do I fix it?

Edit 3

In my application.js, I included Flickity accordingly.

import '../src/flickity.pkgd.min'

Also, my package.json looks like this:

{
  "name": "myapp",
  "private": true,
  "dependencies": {
    "@rails/actioncable": "^6.0.0",
    "@rails/actiontext": "^6.0.2-2",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "4.2.2",
    "bootstrap": "^4.4.1",
    "data-confirm-modal": "^1.6.2",
    "expose-loader": "^0.7.5",
    "flickity": "^2.2.1",
    "jquery": "^3.5.0",
    "local-time": "^2.1.0",
    "popper.js": "^1.16.1",
    "stimulus": "^1.1.1",
    "trix": "^1.0.0",
    "turbolinks": "^5.2.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3.10.3"
  }
}

Edit 4

Here is what my application.js looks like:

import '../src/flickity.pkgd.min'

$(document).on("turbolinks:load", () => {
  var Flickity = require('flickity');
  var flkty = new Flickity( 'section[data-flickity]', {
      imagesLoaded: true,
      wrapAround: true,
  });
})

Here is the error I get on the first page load in my JS console.

first-load-errors-flickity

Edit 5

Everything is more or less completely fixed, but I am still seeing a double reload whenever I go back to a page that was previously loaded with Flickity.

For example consider the following flow:

  • Go to home page with Flickity (load once)
  • Go to Article#New without Flickity (load once and Flickity doesn't load or throw JS error per latest update).
  • Go back to home page with Flickity (it starts to load and about 1/4 or 1/3 way through that initial page load it does a complete page refresh and loads the entire page). That page refresh is noticeable because it happens about 1 - 2 seconds after the page starts loading.

I initially thought it was because I am initializing Flickity twice, i.e. once with the JS in the answer below and once via HTML like so:

<section id="featured-header" class="controls-inside controls-light p-0" data-flickity='{ "imagesLoaded": true, "wrapAround": true }'>

So I swapped the JS selector for the id of this tag and removed the HTML initialization of data-flickity but it still does the same behavior. i.e. I tested this code instead:

<section id="featured-header" class="controls-inside controls-light p-0">

So that leads me to believe it is something to do with the JS.

By the way, it seems that it actually just reloads that section[data-flickity] not necessarily the whole page. It also doesn't reload any other page when I navigate through various links. This only happens for the section[data-flickity] element.


Solution

  • You can remove the JS file that you have in your app src directory and install it using NPM

    npm install flickity
    

    Then, require Flickity CSS file in application.css

    *= require flickity.css
    

    Then, in you JS file you can initialize Flickity on turbolinks:load as you done for tooltip and popover.

    import Flickity from 'flickity';
    
    $(document).on("turbolinks:load", () => {
        if($('section[data-flickity]').length > 0) {
            var flkty = new Flickity( 'section[data-flickity]', {
                imagesLoaded: true,
                wrapAround: true,
            });
        }
    })