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

39,449

Solution 1

#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.

Solution 2

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>

Solution 3

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/

Solution 4

CSS fix

.fb-like, 
.fb-like > span,
.fb-like > span iframe {
    max-width:273px;
}

Solution 5

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.

Share:
39,449
Josh Leitzel
Author by

Josh Leitzel

Ruby ± Rails, JavaScript | CoffeeScript | Angular I can be contacted at [email protected]. Twitter GitHub Blog

Updated on May 12, 2020

Comments

  • Josh Leitzel
    Josh Leitzel almost 4 years

    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!