Skip to main content

How do I add a conditional snippet of page specific javascript (likely a turbolinks issue)?

Javascript • Asked by Peter Marcano

Fallback

Here's to hoping I can be clear and concise about my problem. I am willing to venmo/paypal a beer's worth of cash to anyone who can help me 🍻

I currently have a bootstrap (3.3.7) modal that is, by default, inactive on the index page of a controller. It's just sitting there, waiting to pop up when I click a link.

In a scenario, I want that modal to automatically pop up (no link clicking required) when aformentioned index action is navigated to and a set of conditions are met.

On the index action of a controller (let's call it the "LearnController" for filename's sake), I perform a little bit of logic that compares cookie and database data to determine whether an instance variable holds a value of true or false in the index.html.erb view and conditionally runs some javascript to force open the modal.

# app/controllers/learn.rb
class LearnController < ApplicationController
  def index
      ...
      @show_modal = do_logic
    end

    private

    def do_logic
      # ... looks at cookies and database to 
        # do some logic and return true or false...
    end
end

# app/views/learn/index.html.erb
<% if @show_modal %>
    <script>
        document.addEventListener("turbolinks:load", function() {
            $('#emailModal').modal('show');
        }
    </script>
<% end %>

I am able to test out the do_logic I had written. It successfully compares the database & cookie data in the ways I want, and returns a true or false at appropriate times. Domain logic is good.

My issue now lies somewhere between my javascript architecture and Turbolinks.

When the controller makes the @show_modal = true, the modal shows up, I fill out the email form or dismiss it entirely. In either event, the cookie is updated to make sure the @show_modal returns false and won't return true again until other conditions are met in the future.

But, when I navigate around the app and back to the learn#index page, @show_modal returns false but the turbolinks:load event still fires and displays the modal when no javascript on the page asked it to.

If I refresh the page in the browser it seems to reload the turbolinks cache and everything works as designed until the next time @show_modal returns true and the browser caches that turbolinks:load behavior again and fires when it shouldn't.

Some interesting things I've found:

  1. Inspecting the page after the modal opens when it shouldn't, my <script> tags aren't present in the DOM. The Rails learn#index doesn't render that text again, yet the behavior stays until the cache is reloaded via a refresh on the browser.
  2. Not only does triggering a reload in the browser fix the issue, but adding <meta name="turbolinks-visit-control" content="reload"> to that specific page also seems to ensure that my code behaves properly. I don't like this solution because this index page is visited more than any page on the app and having turbolinks disabled on the most visited page makes the user experience slow and strange where it shouldn't be.

Do you see an obvious answer to this problem, or maybe you've had a similar struggle but implemented this one-off javascript differently?

Thanks!


Fallback

Try adding some JS to hide the modal on the turbolinks:before-cache event so it doesn't ever get saved when open.


Fallback

I like your thinking, unfortunately it doesn't work :(

My temporary solution is now:

<% if @show_modal %>
        <% content_for :head do %>
            <meta name="turbolinks-visit-control" content="reload">
        <% end %>

    <script>
            document.addEventListener("turbolinks:load", function() {
                $('#emailModal').modal('show');
            }
    </script>
<% end %>

Which isn't ideal as it make forces a full page reload upon navigating back to the page, but at least it doesn't reload every visit.


Fallback

This would be slightly slower, but what if instead of opening the modal on page load, you make an AJAX request to retrieve the value of @show_modal, and then depending on the response, you open the modal?

That way you're not trying dealing with any cache related things at all.


Fallback

That could work? I'll test it later and let you know.

I'm surprised that Basecamp could develop a frameworks like Rails and Turbolinks and not account for SJR.

I did try to pass an index.js.erb file to do this work as well but it didn't render the modal properly... may be a problem with bootstrap dependencies?... I just don't know anymore 🙄


Login or create an account to join the conversation.