Question

I have a fb-like-box and a twitter-timeline, the official facebook page and twitter widgets. However, on actual navigation (the links in the sidebar do an ajax load), for example on clicking the logo, these two widgets vanish into thin air.

On refreshing the page, the widgets once again appear as expected.

<div id="social_bar">
    <div id="facebook_like_box">
        <div class="fb-like-box" data-href="https://www.facebook.com/Gx.Edg" data-width="234" data-height="500" data-colorscheme="light" data-show-faces="true" data-header="true" data-stream="false" data-show-border="true"></div>
    </div>

    <a class="twitter-timeline" href="https://twitter.com/gxEDGE" data-widget-id="308092518074040321"></a>
    <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</div>
Was it helpful?

Solution

The root cause is how Turbolinks interacts with header scripts. @peteykun offers the correct solution in his answer, though it is out of date and lacks an elaborate explanation. At the time of this writing, Rails 5 uses Turbolinks 5.

When you visit a page on your Rails site for the first time (or refresh the page), the page is fetched normally without interference from Turbolinks. The expected browser events fire, the JavaScript executes, and everything renders properly. The trouble starts when you navigate from that page to another page on the same domain (i.e. clicking a link). When that happens, Turbolinks intercepts the new page request and makes a slightly different request that is faster. Glossing over the details, the end result is that expected browser events do not fire as they normally would. No events, no JavaScript. No JavaScript, no rendering. Per the Turbolinks GitHub:

You may be used to installing JavaScript behavior in response to the window.onload, DOMContentLoaded, or jQuery ready events. With Turbolinks, these events will fire only in response to the initial page load—not after any subsequent page changes.

The best and simplest solution is to listen for Turbolink's events instead of the standard browser events.

Fortunately, there's a website authored by user @reed of GitHub which is dedicated to rewriting a wide variety of commonly-used JavaScripts to be compatible with Turbolinks. Unfortunately, the solutions currently on the site were written for an earlier version of Turbolinks and will not work out-of-the-box. Assuming they haven't updated the site yet, you'll need to make some changes.

First, change all event bindings of the form page:* to turbolinks:*. The event namespaces were changed in Turbolinks 5. This is the only change required to get @reed's Twitter coffeescript (Solution #2) to work. As for the Facebook coffeescript, there's no such thing as a turbolinks:fetch or turbolinks:change event, but that's okay. Read on for a more elegant solution.

Reed's Facebook coffeescript uses Javascript to transfer the #fb-root element from the body of the old page to the new one. As I understand it, this is done to preserve the Facebook functionality. Turbolinks 5 has a new feature that allows you to mark a page element as permanent. User @pomartel mentioned this here, and I believe this is a much more elegant solution. Simply add the data-turbolinks-permanent attribute to the #fb-root div, and we remove the need for the saveFacebookRoot and restoreFacebookRoot functions.

The complete Facebook solution is below. In the <body>:

<body>
  <div id="fb-root" data-turbolinks-permanent></div>

In the <head>:

<%= javascript_include_tag 'facebook_sdk', 'data-turbolinks-track': 'reload' %>

And the facebook_sdk.coffee file, which belongs in ./app/assets/javascripts:

$ ->
  loadFacebookSDK()
  bindFacebookEvents() unless window.fbEventsBound

bindFacebookEvents = ->
  $(document)
    .on('turbolinks:load', ->
      FB?.XFBML.parse()
    )
  @fbEventsBound = true

loadFacebookSDK = ->
  window.fbAsyncInit = initializeFacebookSDK
  $.getScript("//connect.facebook.net/en_US/sdk.js")

initializeFacebookSDK = ->
  FB.init
    appId  : 'YOUR-APP-ID-HERE'
    status : true
    cookie : true
    xfbml  : true
    autoLogAppEvents : true
    version: 'v2.11'

Don't forget to add the Facebook and Twitter coffeescripts to the precompiled asset pipeline!

OTHER TIPS

The problem was a ruby gem that is bundled by default, called Turbolinks.

The solution is found in the Turbolinks Compatibility Project:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top