Turbolinks: facebook page it not display on turbolinks 5

Created on 22 Dec 2016  路  17Comments  路  Source: turbolinks/turbolinks

I used to use this code on my project and it works fine, but when I update to turbolinks 5 it stop display my FB page.

$ ->
  loadFacebookSDK()
  bindFacebookEvents() unless window.fbEventsBound
bindFacebookEvents = ->
  $(document)
    .on('page:fetch', saveFacebookRoot)
    .on('page:change', restoreFacebookRoot)
    .on('page:update', ->
      FB?.XFBML.parse()
    )
    .on('page:load', ->
      FB?.XFBML.parse()
    )
  @fbEventsBound = true

saveFacebookRoot = ->
  if $('#fb-root').length
    @fbRoot = $('#fb-root').detach()

restoreFacebookRoot = ->
  if @fbRoot?
    if $('#fb-root').length
      $('#fb-root').replaceWith @fbRoot
    else
      $('body').append @fbRoot

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

initializeFacebookSDK = ->
  FB.init
    appId  : 'xxxxxx'
    version: 'v2.5'
    status : true
    cookie : true
    xfbml  : true

Does anyone know how to fix it?

Most helpful comment

You can use this script to solve this problem:

https://gist.github.com/6temes/52648dc6b3adbbf05da3942794b97a00

// FacebookSDK
// https://developers.facebook.com/docs/plugins/page-plugin/
(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s);
  js.id = id;
  js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk')); // Replace 'facebook-jssdk' with your page id.

// Compatibility with Turbolinks 5
(function($) {
  var fbRoot;

  function saveFacebookRoot() {
    if ($('#fb-root').length) {
      fbRoot = $('#fb-root').detach();
    }
  };

  function restoreFacebookRoot() {
    if (fbRoot != null) {
      if ($('#fb-root').length) {
        $('#fb-root').replaceWith(fbRoot);
      } else {
        $('body').append(fbRoot);
      }
    }

    if (typeof FB !== "undefined" && FB !== null) { // Instance of FacebookSDK
      FB.XFBML.parse();
    }
  };

  document.addEventListener('turbolinks:request-start', saveFacebookRoot)
  document.addEventListener('turbolinks:load', restoreFacebookRoot)
}(jQuery));

All 17 comments

I'm not familiar with the coffeescript nor FB's api, but I believe that all of this functionality is dependent on:

$ ->

The documentation states you may have to adjust your jQuery to listen to turbolinks-specific events such as turbolinks:load or turbolinks:visit and I believe, after a brief view of turbolinks-classic readme, the API has changed significantly. Find out more on their readme here and here.

If I'm right, your page is being loaded by turbolinks, which means the page is not fully reloaded which means your jQuery.ready function is never invoked, so use the turbolinks:load instead. This will cover both turbo links navigation as well as full page refreshes.

You can use this script to solve this problem:

https://gist.github.com/6temes/52648dc6b3adbbf05da3942794b97a00

// FacebookSDK
// https://developers.facebook.com/docs/plugins/page-plugin/
(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s);
  js.id = id;
  js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.8";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk')); // Replace 'facebook-jssdk' with your page id.

// Compatibility with Turbolinks 5
(function($) {
  var fbRoot;

  function saveFacebookRoot() {
    if ($('#fb-root').length) {
      fbRoot = $('#fb-root').detach();
    }
  };

  function restoreFacebookRoot() {
    if (fbRoot != null) {
      if ($('#fb-root').length) {
        $('#fb-root').replaceWith(fbRoot);
      } else {
        $('body').append(fbRoot);
      }
    }

    if (typeof FB !== "undefined" && FB !== null) { // Instance of FacebookSDK
      FB.XFBML.parse();
    }
  };

  document.addEventListener('turbolinks:request-start', saveFacebookRoot)
  document.addEventListener('turbolinks:load', restoreFacebookRoot)
}(jQuery));

@6temes Also make a note about jp_JP part in "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v2.8".
I was wondering why it switched to japanese for a while. :p This works fine though, thanks!

With Turbolinks 5, you don't have to save and restore the Facebook root element. You just have to make it permanent and it will stick between requests: https://github.com/turbolinks/turbolinks#persisting-elements-across-page-loads

@Tashows Upps... Edited. :)

@pomartel The issue I've been having goes like this: I have a facebook page feed placed in a page of my app. The first time I visit that page, the feed loads fine. If I refresh that page, it also works. If I navigate to another page and then come back, the facebook feed won't load. I can't seem to solve this with the data-turbolinks-permanent feature. But then again, I am not sure if this is the intended use anyway.

With the code from 6temes, when I revisit the first page, I see the feed for half a second (I guess from the cached page), then it goes away and reappears once facebook loads again. Not really sure how that should work, or what I 'm doing wrong. Is that flickering to be expected?

I confirm that it happens the same to me. I just didn't realize because, in my page, the applet is in the bottom of the page.

@Tashows It depends how that Facebook page feed is loaded. If it's FBXML then you might want to parse it using FB.XFBML.parse() on the turbolinks:load event. The Facebook JS SDK will do it once it loads but won't do it afterwards.

So I had the same issue and I tried to understand the problem based on @6temes solution and @pomartel mention of data-turbolinks-permanent

Here is what I ended up doing if some people want an alternative using the data-turbolinks-permanent feature of turbolinks . https://github.com/turbolinks/turbolinks#persisting-elements-across-page-loads

I am using middleman.
I have a layout at the bottom of my body tag. Notice the data-turbolinks-permanent attribute on the div#permanent

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

Then here is my javascript. I am using jQuery

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

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

$(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();
  }
});

Just after the FB.init() I am moving the #fb-root to the div#permanent.
That way we avoid triggering a replacement for each visit. Instead we persist the #fb-root by putting it in a div with a data-turbolinks-permanent attribute. It seems to work so far.

_I tried to append the data-turbolinks-permanent to the #fb-root but that does not work._

@AlexB52 This works fine and even though it didn't solve the flickering problem (it probably shouldn't have anyway), it helped with the proper size of the page plugin. Now it respects its parent's width, while before it wouldn't when it reloaded. It's also faster than the previous solution, which makes sense I guess.

I tried adding the data-turbolinks-permanent to the plugin's container as well and then it seems to load properly from the cached version, but then when the turbolinks:load event fires the FB.XFBML.parse() makes it reload again. However if I don't run that parse() then it doesn't display when you visit another page. Not sure what the workaround is.

To avoid that flickering I ended up removing the plugin's container before caching the page. That doesn't really feel like a proper permanent solution though.

$(document).on("turbolinks:before-cache", function() {
    $(".facebook-feed-wrap").remove();
});

@Tashows I understand the problem now. I do have the flickering on development environment but not staging online.

Based on your code, what about adding this then?

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

Then on all your facebook plugin you can add that attribute like this:

<div data-turoblinks-nocache 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>

That way all elements that do not need to be cached are removed before caching. This can apply to any elements not necessarily restricted to Facebook plugin. I am unsure about performance here. It feels a bit bad. I guess you could restrict on a per page basis where you need that specific behaviour.

Apparently no-caching is enabled for a complete page but not per element. Maybe data-turoblinks-nocache for elements could be a pr to add that behaviour to turbolinks library. Maybe this is done on purpose. I don't know

Also it feels that this problem is a good candidate for this part of turbolinks documentation: https://github.com/turbolinks/turbolinks#responding-to-page-updates

@AlexB52 I was getting the flickering on production too. And yes, that sure is a better way to target such containers.

A mutation observer could be a better solution, however I didn't (and don't) feel confident enough to go that way. Mainly worried about lack of control since facebook is doing all the loading/parsing. A quick test based on this, failed: https://developers.facebook.com/docs/reference/javascript/FB.Canvas.setDoneLoading
I am not going to spend more time on this for the time being. I am happy enough with the cache control thingy. Thanks!

btw you don't need that permanent div, you may have

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

and remove that extra js line :)

@rafbgarcia Have you actually implemented your solution? It is not working for me.

yep

script.js.erb

function FBInit() {
  FB.init({
    appId      : '<%= Rails.env.production? ? '1' : '2' %>',
    xfbml      : true,
    version    : 'v2.8'
  });
};

$(document).ready(function(){
  $.getScript( "//connect.facebook.net/pt_BR/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();
});

layouts/application.html.erb

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

  <div data-turbolinks-no-cache class="fb-share-button" data-href="#{request.url}" data-layout="button" data-size="large" data-mobile-iframe="true"><a class="fb-xfbml-parse-ignore" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fdevelopers.facebook.com%2Fdocs%2Fplugins%2F&amp;src=sdkpreparse">Share</a></div>

Got it. I thought you meant the whole Turbolinks compatibility JS section is not needed. Not just the fb-root detach/append. Now it is working fine. Thanks!

It seems like this issue has been resolved. Closing for now.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nerdcave picture nerdcave  路  16Comments

tadiou picture tadiou  路  42Comments

wayneashleyberry picture wayneashleyberry  路  16Comments

stefatkins picture stefatkins  路  17Comments

AipackGit picture AipackGit  路  12Comments