Is there a way to detect if the Facebook Javascript SDK loaded successfully?
I use Facebook as the membership system of 开发者_高级运维my website. It uses the code to generate a login control, allowing users to login via their facebook account. It's essentially one click if they're already a member, 2 if they're not (for granting permissions).
I got a problem though... feedback is suggesting the login button isn't always loading correctly. Instead of loading the facebook login control, it simply states (in text) "login via facebook" - which is what the login button would say if the control loaded successfully.
Testing shows that is what happens when the facebook javascript SDK fails to load completely (for whatever reason). I've seen instances where a # in the url prevents the SDK from loading.
To better support this issue, how would I go about detecting if the facebook javascript SDK loaded, and is ready? That way, if it fails, I can leave some sort of note for the user.
Here's how it's currently added to the page:
<script>
window.fbAsyncInit = function () {
FB.init({
appId: '***************',
status: true,
cookie: true,
xfbml: true
});
FB.Event.subscribe('auth.login', function (response) {
window.location.reload();
});
};
(function () {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
} ());
</script>
You should load the Javascript Library Asynchronously and put all your FB related functions inside the window.fbAsyncInit
method:
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : 'YOUR_APP_ID', // App ID
channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
// Additional initialization code here
};
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
}(document));
</script>
This code loads the SDK asynchronously so it does not block loading other elements of your page. This is particularly important to ensure fast page loads for users and SEO robots.
The URLs in the above code are protocol relative. This lets the browser to load the SDK over the same protocol (HTTP or HTTPS) as the containing page, which will prevent "Insecure Content" warnings.
The function assigned to
window.fbAsyncInit
is run as soon as the SDK is loaded. Any code that you want to run after the SDK is loaded should be placed within this function and after the call toFB.init
. For example, this is where you would test the logged in status of the user or subscribe to any Facebook events in which your application is interested.
A quick example is the following:
<div id="fb-root"></div>
<script>
var isLoaded = false;
window.fbAsyncInit = function() {
FB.init({
appId : 'YOUR_APP_ID', // App ID
channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
isLoaded = true;
// Additional initialization code here
};
function checkIfLoaded() {
if(isLoaded) console.log("LOADED!");
else console.log("NOT YET!");
return false;
}
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
}(document));
</script>
<a href="#" onclick="checkIfLoaded();">Check</a>
(Just clicked the
check
link a couple of times)
Please note that you can still construct the Login Link server-side and WITHOUT JavaScript. Example using the PHP-SDK:
$loginUrl = $facebook->getLoginUrl();
...
...
<a href="<?php echo $loginUrl; ?>">
<img src="http://static.ak.fbcdn.net/rsrc.php/zB6N8/hash/4li2k73z.gif">
</a>
Trigger an event when the SDK is loaded:
window.fbAsyncInit = function() {
FB.init({appId: "#{KeyManager.facebook_app_id}", status: true, cookie: true, xfbml: true});
jQuery('#fb-root').trigger('facebook:init');
};
And listen for the event like this:
$("#fb-root").bind("facebook:init", function() {
..
});
If you are using jQuery (and you have loaded jQuery prior to the FB initialization) you can use a Deferred to run additional initialization.
<script>
window.fbLoaded = $.Deferred();
window.fbAsyncInit = function() {
FB.init({
appId : '----------',
xfbml : true,
status : true,
version : 'v2.7'
});
window.fbLoaded.resolve();
};
(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";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
Then elsewhere (in a JS file) you can do this (in fact the key of this method is you can put this in as many places as you want and they will all get triggered):
window.fbLoaded.done(function () { alert('FB initialized'); });
Note: If the initialization completes BEFORE you add the done
event it will fire immediately (that is how Deferreds work). So you can put it wherever you want.
Be sure to test what you want the behavior to be if the API is never initializaed (just comment out the (function(d,s,id)... part
Just look for FB object after loading FB JS SDK sync/async as given:
if (typeof FB !== 'undefined') {
alert("FB JS API is available now");
} else {alert("do something...")}
That's a enough check to call any Facebook JS API related method safely.
Promise-based version, for modern browsers
// Load Facebook JS SDK
(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 = 'https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.11';
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
// Create a new promise, set to resolve using the global function `fbAsyncInit`,
// which the Facebook JS SDK will call once it's ready.
new Promise((resolve, reject) => {
window.fbAsyncInit = resolve;
}).then(() => {
// ... your code here ...
});
What globals does this JS file introduce?
Let's say it creates a
var FaceBook = function() {//make friends! Or whatever...}
You can test for if (FaceBook)
and go from there.
It seems likely that they give you some sort of load event for when their framework is booted up, so to speak.
According to this page http://www.techsirius.com/2014/04/detect-social-SDK-didnt-load.html
$(window).load(function(){
if(typeof window.FB == 'undefined'){
alert('Facebook SDK is unable to load, display some alternative content for visitor');
}
else{
alert('Facebook is working just fine');
}
});
Load the script dynamically (add a script tag from js) and check for error / success events.
var FB;
jQuery.fbInit = function(app_id) {
window.fbAsyncInit = function() {
FB.init({
appId : app_id,
status : true,
channelUrl: '#',
cookie : true,
xfbml : true
});
FB = FB;
getLoginStatus();
};
// Load the SDK Asynchronously
(function(d){
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/it_IT/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
}(document));
$('<div />').attr('id','fb-root').appendTo('body');
};
/**
* https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus/
*/
function getLoginStatus() {
FB.getLoginStatus(function(response) {
if (response.status === 'connected') {
$('#fb-root').trigger('facebook:init',[response]);
} else if (response.status === 'not_authorized') {
$('#fb-root').trigger('facebook:init',[response]);
} else {
$('#fb-root').trigger('facebook:init',[response]);
}
});
}
If you want check the status from another javascript (with the suggestion of @Leo Romanovsky )
$("#fb-root").on("facebook:init", function(event, response) {
if(response.status === 'connected') {
alert("logged with FB")
}else{
alert("No Logged with FB")
}
});
I use this from few days :
var isLoaded = false;
$(document).on('DOMSubtreeModified', function () {
if ($('#fb-root').length && !isLoaded) {
isLoaded = true;
// ...
}
});
What do you think ?
As stated in other answers; with thanks to @ifaour & @Leo Romanovsky; the best way to detect if the SDK is loaded successully is to trigger an event when the fbAsyncInit is called.
If the event is captured then the SDK is loaded. The code below shows how this could be achieved:
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : 'YOUR_APP_ID', // App ID
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
// Fire an event when the FB library asynchronously is loaded
$("#fb-root").trigger("facebook:init");
// Additional initialization code here
};
// Load the SDK Asynchronously
(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";
fjs.parentNode.insertBefore(js, fjs);
}
(document, "script", "facebook-jssdk"));
</script>
// Somewhere else in the code
$(document).ready(function () {
$("#fb-root").bind("facebook:init", function () {
console.log("The event is fired FB SDK object is ready to be used.");
});
});
In the event that the event is not fired you could add a timer to check if FB object is 'undefined' and show the appropriate message.
Note 1: Downside there could a slight delay till this event is fired.
Note 2: "feedback is suggesting the login button isn't always loading correctly". I know that FFv49 private browsing blocks the call to fb.
精彩评论