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>
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!