Resource | Implementing Facebook SDK with Turbolinks 5

Implementing Facebook SDK with Turbolinks 5

I am using the Turbolinks library on a static website to get a snappy feeling when navigating, similar to a single-page application.

Bridging Turbolinks and Facebook SDK

Bridging Turbolinks and Facebook SDK

I came across some difficulties when using the Javascript SDK in a Turbolinks context to render multiple Facebook plugins on several pages. The elements would render on first hit and not render on any other page. When finally rendered across all pages, the elements would blink between the cached preview and the final rendering.

TL;DR - Here is the final implementation using the data-turbolinks-permanent feature. Scroll down for the explanation.

# /source/layouts/layout.erb
<body class="<%= page_classes %>">
  <%= yield %>
  <div id='permanent' data-turbolinks-permanent></div>
</body>
function FBInit() {
  FB.init({
    appId      : 'YOUR_KEY',
    xfbml      : true,
    version    : 'v2.8'
  });
  $('#permanent').append( $('#fb-root').detach() );
};

$(document).ready(function(){
  $.getScript( "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8", FBInit);
});

$(document).on('turbolinks:load', function(event){
  if (typeof FB !== "undefined" && FB !== null) {
    FB.XFBML.parse();
  }
});

$(document).on("turbolinks:before-cache", function() {
    $('[data-turbolinks-no-cache]').remove();
});

Then use any Facebook plugins using the data-turbolinks-no-cache attribute like this:

<div data-turbolinks-no-cache 
  class="fb-like" 
  data-href="#" 
  data-layout="standard" 
  data-action="like" 
  data-size="small" 
  data-show-faces="true" 
  data-share="true"></div>

Explanation

Rendering all Facebook plugins on pages

I am using Middleman to generate all the assets and, like most frameworks, there is a layout template where the body content goes. A <div> tag has been added right before the </body> tag.

# /source/layouts/layout.erb

<body class="<%= page_classes %>">
  <%= yield %>
  <div id='permanent' data-turbolinks-permanent></div>
</body>

Notice the data-turbolinks-permanent attribute on the div#permanent

This allows any content on that div to persist and be passed on to all the other pages you will navigate to. See the feature on documentation.

Here is the javascript using jQuery for convenience.

# /source/javascripts/_facebook.js

function FBInit() {
  FB.init({
    appId      : 'YOUR_KEY',
    xfbml      : true,
    version    : 'v2.8'
  });

  $('#permanent').append( $('#fb-root').detach() );
};

$(document).ready(function(){
  $.getScript( "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8", FBInit);
});

Calling FB.init() after the library is downloaded will append the#fb-root to the bottom of the page. The responsibility of that element is to render Facebook plugins and therefore needs to appear on any page with a Facebook plugin.

After FB.init() the #fb-root element is moved to the div#permanent from the layout.

As a result the #fb-root is persisted across all pages.

Then we render the plugins on each page.

$(document).on('turbolinks:load', function(event){
  if (typeof FB !== "undefined" && FB !== null) {
    FB.XFBML.parse();
  }
});

This adds an event listener to trigger (on each visit) the parsing function that renders all Facebook plugins in the current page. See the list of available events for turbolinks.

Now all Facebook plugins will be rendered.

Solve the flickering effect

Another problem appears when coming back to a previous page that holds a Facebook plugin. The page blinks as the preview shows the rendered plugin, and then turbolinks:load event re-renders it, causing the plugin to disappear and reappear in a fraction of a second.

To avoid this you can add an attribute data-turbolinks-no-cache to all the Facebook plugins like this:

<div data-turbolinks-no-cache 
  class="fb-like" 
  data-href="[#](http://localhost:4567/)" 
  data-layout="standard" 
  data-action="like" 
  data-size="small" 
  data-show-faces="true" 
  data-share="true"></div>

And in your javascript:

$(document).on("turbolinks:before-cache", function() {
    $('[data-turbolinks-no-cache]').remove();
});

This allows any elements with the data-turbolinks-no-cache attribute to be removed from the turbolinks cached preview of the page.

The data-turbolinks-no-cache attribute is just a placeholder and any name can be used as it is not part of the turbolinks core library.

Also, the attribute can be added to any DOM element that needs to be removed from a cached preview.

Done !

Now it is possible for you to use the FB object and render Facebook plugins on all your pages.

I decided on this implementation, based on this conversation.

Message sent
Message could not be sent
|