How can I make the Facebook Like button's width automatically resize?
Asked Answered
A

8

32

I'm implementing the Facebook Like Button, but I'm running into some trouble with the width. I'm using the JavaScript SDK implementation, not the direct iframe.

According to the documentation, the default width is 450. That's fine, and I understand that the width can be changed by way of the width attribute on the <fb:like> tag. However, my problem is that I really cannot specify a fixed width. Due to the nature of the button, the width is not constant in all states. For example, if no one has liked the page yet it displays "Be the first of your friends to like this"; if someone has, it displays "XXX people like this. Be the first of your friends"; and still if you have liked it, it displays "You like this" or "You and XXX people like this." In other words, there are many states of the button, none of which share a constant width.

This wouldn't be much of a problem if it weren't for the fact that I want to display the button floated on the right-hand side of a <div>. To be clearer, this is what I'm doing:

<div id="wrapper">
    <span class="fb-like"><fb:like show_faces="false" width="450" font="lucida grande""></fb:like></span>
    ...
</div>
<style type="text/css">
.fblike {
    display: inline-block;
    padding: 0.5em;
    position: absolute;
    right: 0;
    top: 0;
}
#wrapper {
    position: relative;
}
</style>

This works fine, but the problem is that the iframe now has a constant width of 450 pixels. Since the iframe is left-aligned, when the text is shorter there is extra space to the right. I've tried various applications of text-align: right to no avail. And the problem is further compounded by the fact that this is really just fancy markup for an iframe that is added by the FB SDK, so I'm powerless to change any of its contents with CSS or JavaScript.

I need a solution that will either (a) keep the width of the button area dynamic (i.e., it changes according to the content); or (b) right-align everything in the button area.

Thanks for any help anyone can give me!

Arched answered 3/12, 2010 at 17:36 Comment(0)
S
13
#fblike iframe {
    width: 95px !important;
}

#fblike .fb_edge_comment_widget iframe {
    width: 330px !important;
}

And

<div id="fblike"><fb:like show-faces="false" layout="button_count"></fb:like></div>

This way both comment and like button iframes are fixed width. No funny effects. Hope it helps.

Spinescent answered 28/1, 2011 at 10:59 Comment(1)
This actually only resizes the iframe, but still cuts some text inside it, since some of the deeper tags within iframe still have CSS width assignments that you can't modify outside of the document.Boston
B
5

As everyone probably knows by now, there is no easy way to do this. I did come up with a programatic kludge of sorts, though. And when I say kludge, I really mean it! I do not consider this ready for prime time, but someone may like to tinker with the concept and try to find something workable.

The idea is that although you can't read the content width of the iframe, you can loop through a series of widths for the iframe itself until you find one that just barely prevents the text inside from wrapping. At that point, the text must touching the right-hand side of the iframe. In other words, we want to set the width of the iframe to 1px wider than the width that would cause the text to wrap.

Detecting whether the text is wrapping is easy enough in principle -- you set the iframe width, wait for the FB code to adjust the content, and then read the height. If everything fit on one line, the height should be about 25px. More than that means the text has wrapped.

The difficulty comes with the "wait for the FB code to adjust the content" part. I feel like there must be someway to force a "redraw" of the iframe, but so far I haven't found it. Calling FB.XFBML.parse() is obvious, but it doesn't seem to work. This is the part where I got stuck. I'm forcing the iframe to reload by setting its src attribute, which does the job but at a horrible price in speed. It is just meant as "proof of concept" at this point. Almost as good would be a simple way to know when any redraw was finished; I feel that should also be possible but my brain bogged down before I found anything simple.

Anyway here is some test code if you want to give it a try. It takes forever to get the button in position, but it does at least work. I left everything visible during the loading process so you can see what it's doing, on a real page it would be better to keep things hidden until everything is ready. Also note that the alignment may be slightly off because I am adjusting the width 5px at a time. If things could be made faster it would be easy to use 1px instead. Even better would probably be a rough adjustment to get close, then a fine adjustment to get it perfect. Obviously lots of experimenting to do, for whoever might want to take it up.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type='text/javascript'>
var likediv, likeframe;
function loadcode(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/all.js#xfbml=1";
  fjs.parentNode.insertBefore(js, fjs);
}
function init()
{
loadcode(document, 'script', 'facebook-jssdk');
likediv=document.getElementById('d2');
setTimeout(initx, 10);
}
function initx()
{
likeframe=likediv.getElementsByTagName('iframe')[0];
if (!likeframe) { setTimeout(initx, 10); return; }
likeframe.style.width='225px';
setTimeout(shrink, 10);
}
function shrink()
{
likeframe=likediv.getElementsByTagName('iframe')[0];
var currwidth=parseInt(likeframe.style.width);
if (currwidth>=500) return;
newwidth=currwidth+5;
likeframe.style.width=newwidth+'px';
likeframe.style.height='0';
likeframe.src=likeframe.src;
setTimeout(checkframe, 10);
}
function checkframe()
{
var h=parseInt(likeframe.offsetHeight);
if (h==0) setTimeout(checkframe, 10);
else if (h>25) shrink();
//else we are done; make the frame visible if we hid it earlier
}
</script>
</head>
<body onload='init()' style='margin:10px 50px'>
<div id="fb-root"></div>
<div style='text-align:right'>Here is some right-aligned text to compare to.</div>
<div id='d2' style='float:right;height:25px;overflow:hidden;border:1px dotted red'>
<div class="fb-like" data-send="false" data-width="225" data-show-faces="false" data-action="recommend"></div>
</div>
</body>
</html>

EDIT: Did a little more experimenting, still an awkward workaround but faster than before:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type='text/javascript'>
var likediv, likeframe, targwidth;
function loadcode(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/all.js#xfbml=1";
  fjs.parentNode.insertBefore(js, fjs);
}
function init()
{
loadcode(document, 'script', 'facebook-jssdk');
likediv=document.getElementById('d2');
setTimeout(initx, 10);
}
function initx()
{
likeframe=likediv.getElementsByTagName('iframe')[0];
if (!likeframe) { setTimeout(initx, 10); return; }
likeframe.style.width='225px';
setTimeout(shrink, 10);
}
function shrink()
{
likeframe=likediv.getElementsByTagName('iframe')[0];
var currwidth=parseInt(likeframe.style.width);
if (currwidth>=500) return;
targwidth=currwidth+5;
likeframe.style.width='10px';
likeframe.style.height='0';
setTimeout(checkframe, 10);
}
function checkframe()
{
var h=parseInt(likeframe.offsetHeight);
if (h==0) { setTimeout(checkframe, 10); return; }
likeframe.style.width=targwidth+'px';
likeframe.style.height='0';
setTimeout(checkframe2, 10);
}
function checkframe2()
{
var h=parseInt(likeframe.offsetHeight);
if (h==0) setTimeout(checkframe2, 10);
else if (h>25) shrink();
//else we are done; make the frame visible if we hid it earlier
}
</script>
</head>
<body onload='init()' style='margin:10px 50px'>
<div id="fb-root"></div>
<div style='text-align:right'>Here is some right-aligned text to compare to.</div>
<div id='d2' style='float:right;height:25px;overflow:hidden;border:1px dotted red'>
<div class="fb-like" data-send="false" data-width="225" data-show-faces="false" data-action="recommend"></div>
</div>
</body>
</html>

FINAL EDIT: This is the best I think this method is ever going to get; the code could certainly be tweaked but it's always going to take several seconds to run through the trial widths to find what works. But it's now quick enough (around 5 seconds) that it might actually be usable. BTW I am adding each new code version rather than replacing the old ones because I haven't done much cross-browser testing of this code and it's possible the higher-speed versions won't work for someone. Since it's all experimental code I think it's better to have the different versions available for fallback.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type='text/javascript'>
var minwidth=225, maxwidth=500, finished=false, last_was_good=null;
var likediv, likeframe, targwidth, boundlow, boundhigh;
function loadcode(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/all.js#xfbml=1";
  fjs.parentNode.insertBefore(js, fjs);
}
function init()
{
loadcode(document, 'script', 'facebook-jssdk');
likediv=document.getElementById('d2');
setTimeout(initx, 10);
}
function initx()
{
likeframe=likediv.getElementsByTagName('iframe')[0];
if (!likeframe) { setTimeout(initx, 10); return; }
likeframe.style.width=minwidth+'px';
setTimeout(trynewwidth, 1);
}
function trynewwidth()
{
if (last_was_good==null) { boundlow=minwidth; boundhigh=maxwidth; }
else if (last_was_good) boundhigh=targwidth;
else boundlow=targwidth;
finished=((boundhigh-boundlow)<2);
if (finished && last_was_good) { done(); return; }
if (finished && !last_was_good) targwidth=boundhigh;
else targwidth=parseInt((boundlow+boundhigh)/2);
setTimeout(setwidth, 1);
}
function done()
{
//All finished, if we were hiding the div make it visible now
}
function setwidth()
{
likeframe=likediv.getElementsByTagName('iframe')[0];
likeframe.style.width='10px';
likeframe.style.height='0';
setTimeout(checkframe, 10);
}
function checkframe()
{
var h=parseInt(likeframe.offsetHeight);
if (h==0) { setTimeout(checkframe, 10); return; }
likeframe.style.width=targwidth+'px';
likeframe.style.height='0';
setTimeout(checkframe2, 10);
}
function checkframe2()
{
var h=parseInt(likeframe.offsetHeight);
if (h==0) { setTimeout(checkframe2, 10); return; }
if (finished) { done(); return; }
last_was_good=(h<26);
setTimeout(trynewwidth, 1);
}
</script>
</head>
<body onload='init()' style='margin:10px 50px'>
<div id="fb-root"></div>
<div style='text-align:right'>Here is some right-aligned text to compare to.</div>
<div id='d2' style='float:right;height:25px;overflow:hidden;border:1px dotted red'>
<div class="fb-like" data-send="false" data-width="225" data-show-faces="false" data-action="recommend"></div>
</div>
</body>
</html>
Boeschen answered 12/10, 2011 at 18:35 Comment(2)
Interesting approach, thanks for sharing. This definitely shows how ridiculous the issue is. Hopefully some Facebook employee swings by and sees this.Wishful
I improved it a bit, new code added above. But I agree, this should not be necessary. All it would take is an attribute that would add a text-align:right to the iframe styling. What's really strange is that apparently this used to be possible by setting the data-width to 0, which made the iframe automatically choose an appropriate width. That clearly doesn't work any more, which implies that it was deliberately removed?!Boeschen
W
5

If you use the XFBML version of the Like button, along with the Facebook Javascript SDK, you can subscribe to the 'xfbml.render' event, and once that event fires, you can set the width of the like button iframe to some small value. (I use 50px.) The iframe will then auto-adjust its width as needed in order to display button and like count and whatever other elements, according to your config.

https://developers.facebook.com/docs/reference/javascript/

The biggest obstacle to this solution is that you will need a Facebook App ID. You can get an App ID by creating an app here: https://developers.facebook.com/apps/

Widener answered 8/4, 2012 at 13:53 Comment(1)
+1 This actually worked (to my surprise). The only problem that remains is that if a user likes, then dislikes, the box will not shrink to fit the "unliked" state, but that'll do for me, not going to waste any more energy on this.Bes
S
3

CSS fix

.fb-like, 
.fb-like > span,
.fb-like > span iframe {
    max-width:273px;
}
Sororate answered 25/8, 2012 at 9:23 Comment(1)
When providing code that solves the problem, it is best to also give at least a short explanation of how it works so that folks reading won't have to mentally parse it line by line to understand the differences.Coptic
C
2

I'm personally using the following...

#fbook-like {
    width: 50px !important; /*width of just the button*/
    overflow: visible; /*so there is no cut off*/
    vertical-align: top; /*bc all other like buttons align top*/
}

What I've done here is put the fb button on the far right and so it aligns with my edge, and just the comments added (dynamic in width) will show past the edge. I'm able to keep a good design while getting the comments showing, regardless of the length.

Caravansary answered 8/9, 2012 at 22:22 Comment(0)
U
2

There are some FAQ answers that are worth reading when choosing a size.

https://developers.facebook.com/docs/plugins/like-button

Although with that said it's still pretty cryptic :

"The width of the plugin. The layout you choose affects the minimum and default widths you can use, please see the FAQ below for more details."

standard

Minimum width: 225 pixels.

Minimum increases by 40px if action is 'recommend' by and increases by 60px if send is 'true'.

Default width: 450 pixels. Height: 35 pixels (without photos) or 80 pixels (with photos).

box_count

Minimum width: 55 pixels. Default width: 55 pixels. Height: 65 pixels.

button_count

Minimum width: 90 pixels. Default width: 90 pixels. Height: 20 pixels.

button

Minimum width: 47 pixels. Default width: 47 pixels. Height: 20 pixels.

Ursal answered 20/5, 2014 at 7:57 Comment(2)
Excellent! This solved my problem of putting a FB button on a page that should be responsive, and should work on a mobile phone 300px wide. The default layout is 450px wide, no matter the settings, and caused a horiz-scrollbar for 150px excess space to the right of the page content. The button_count layout does not cause such scrollbar.Pacific
Note that these numbers are only good for English (?) -- try German or French and nothing is the same.Schnorr
M
0

I had to face this same problem with the add-this plugin and solved in a similar way:

.addthis_button_facebook_like,
.addthis_button_facebook_like span,
.addthis_button_facebook_like span iframe {
    max-width: 450px !important;
}
Medius answered 2/4, 2014 at 14:29 Comment(0)
L
0

I added this to my CSS:

div.fb-like.fb_iframe_widget > span {
 width: 100% !important;
}

div.fb-like.fb_iframe_widget{
 width: 100%;
}
Lem answered 11/5, 2016 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.