Archive for the ‘AJAX’ Category

How the facebook like button works

Friday, August 13th, 2010

I was recently working on a project involving the facebook “like” button and how it creates links back to the place of origin.  For the vast majority of websites, simply implementing the code found in the facebook documentation is enough, however, I was dealing with a different sort of monster.  The site I wanted to implement the like button on was self-replicating, using a url rewrite rule to handle replication.  To prevent duplicate content, a canonical url tag was listed in the header.  I noticed upon implementation of the facebook like button that the posted links were returning to the specified canonical url in the page header instead of the url I specified in my like tag.  This caused me to want to investigate how facebook’s like button works.  Below are my findings.

Upon clicking the “like” button, whether using the <iframe> or js SDK version of the like button, an AJAX call was sent out to facebook (of course).  Facebook would search, in this order, for the first of the following:

  1. facebook’s own open graph protocol meta tag
  2. the canonical url meta tag
  3. a specified url in the actual like button
  4. the current page’s url

Once determining the URL based on the above, facebook would then crawl the correct page sniffing for (in this order):

  1. facebook’s own open graph protocol meta tags
  2. any other data it could glean to get what it needed (ie, a meta description, the title, etc)

It would use the gathered information to then build the post on the user’s wall correctly.  However, in certain circumstances, specifying your own og tag was not enough.

In the event a user clicks the “like” button, but does not enter the optional comment, the link posted on the user’s wall actually linked back to the canonical URL.  Interestingly, the fan page that facebook creates for your page is properly linked using the og:url you specify.  We all know, however, that most users will click on the news feed link instead of the page’s.  Below are the tests I ran to verify what was happening:

canonical OG
tag
comment Finding screenshot Conclusion
YES YES YES properly linked, extra info included 1 Best case scenario but cannot force
someone to comment. OG tags will create fan pages for any page
someone “likes”.
YES YES NO Improperly linked on feed, but properly
linked in all extra info that is included (fig 1)
Note: on page refresh, it appears facebook
crawled the correct web page and added a link to the root of the site:
(still improperly linked (fig 2)
1.2.1

2. 2.2

Could get around with use of a rel param
that would be sent in url. Would require framework change to support.
YES NO YES improperly linked everywhere 3 Current implementation on live.
not acceptable
YES NO NO Improperly linked (fig 1)
Edit: on page refresh, it appears facebook
crawled the canonical index page and picked up the extra info on it
included in the canonical og tags: (still improperly linked, but extra
info from canonical page included) (fig 2)
1.4.1

2. 4.2

Current implementation on live.
not acceptable
NO YES YES Properly Linked, extra info included,
more likely to appear in news feed.
5 cannot implement—canonical must be
defined.
NO YES NO Properly Linked, with
extra info included
6 cannot implement—canonical must be
defined.
NO NO YES Properly linked, extra info
gotten from other meta tags in header
7 cannot implement—canonical must be
defined.
NO NO NO Properly Linked 8 cannot implement—canonical must be
defined.

All tests were run with the facebook javascript SDK Like button implementation method as comments are not
allowed with <iframe>s in the size we need.

Facebook has created a “rel” attribute that can be sent in with a like button, which is returned regardless of the url facebook chooses to show.  Theoretically, the issue of what page facebook posts could be gotten around with a sniffer in your code that looks for a url param of “fb_rel” and redirects accordingly.

AJAX/Javascript/Coldfusion Dependent Dropdowns with Optional Text Box

Friday, December 18th, 2009

Here’s a common problem. You have a form where you want one select box in a form to be dependent on the other. Coldfusion makes this ridiculously easy with its built in AJAX functionality. Let’s take a gander:

For this example, I set up a cfc called “ajax” (clever, eh?). Here is the function inside it…

 
<cffunction name="getstates" access="remote" returntype="array" hint="lookup states for dropdown">
	<cfargument name="country" type="string" required="true" default="">
 
		<!--- Define variables --->
		<cfset var data="">
		<cfset var i=0>
		<cfset var result=ArrayNew(2)>
 
		<!--- Do search --->
		<cfquery datasource="#variables.dsn#" name="data">
		SELECT state,postal_code
		from tblstates
		where country=<cfqueryparam value="#ucase(arguments.country)#" cfsqltype="cf_sql_varchar">
		order by state
		</cfquery>
 
		<!--- Build result array --->
		<!--- Convert results to array --->
        <cfloop index="i" from="1" to="#data.RecordCount#">
            <cfset result[i][1]=data.postal_code[i]>
            <cfset result[i][2]=data.state[i]>
        </cfloop>
 
		<!--- And return it --->
		<cfreturn result>
	</cffunction>

Mmk, Now let’s look at the actual code on my form page:

<!---here's a query for filling my select box...--->
<cfquery name="rscountry" datasource="#application.db#">
SELECT *
FROM tblcountries
ORDER BY country ASC
</cfquery>
 
<!---here's my form--->
<cfform method="post" name="form1" action="join.cfm?dealeruname=#this_dealer_username#">
     <cfselect name="country" id="country" required="yes" message="Please select your country of residence." query="rscountry" value="country" display="country" selected="United States"></cfselect>
 
     <cfselect name="stateprov" bindonload="true" bind="cfc:ajax.getstates({country})" /> 
</cfform>

That’s it, folks. easy.

But what about when I don’t have a State listing for say… Uganda. Well, we have to have some way of getting the state/province if we don’t have a populated dropdown. Now there’s more to do. There are probably more elegant solutions out there, but this is what I did, and it seems to work pretty well.

Now we’re going to need to use some javascript and a bit more ajax. Let’s do it!

First, let’s get our form prepped:

 
<!---here's my sexier form--->
<cfform method="post" name="form1" action="join.cfm?dealeruname=#this_dealer_username#">
     <!---Notice, I've added an onchange event to this field.--->
     <cfselect name="country" id="country" required="yes" message="Please select your country of residence." query="rscountry" value="country" display="country" selected="United States" 
onChange="showwhich()"></cfselect>
 
   <!---wrap each field in a uniquely identified div--->
    <div id="dd_state">
	<cfselect name="stateprov" bindonload="true" bind="cfc:ajax.getstates({country})" /> 
   </div>
   <!---add a new text input, and hide it with the div--->
   <div id="type_state" style="display:none">
	<cfinput name="stateprovt" type="text" />   
   </div>
</cfform>

OK, we have our text field. It’s hidden. We’ve got all the other stuff in place, we just need to write the stuff that makes it work!

First, we need to make a new cffunction in our ajax cfc. The function will return a boolean value for whether or not the country has states/provinces within it. You could probably set this up to work with your first function, but I went with a smaller query to reduce some load.

<cffunction name="arestates" access="remote" returntype="boolean" hint="are there states?">
	<cfargument name="country" type="string" required="true" default="UNITED STATES">
 
		<!--- Define variable --->
		<cfset var data="">
 
		<!--- Do search --->
		<cfquery datasource="#variables.dsn#" name="data">
		SELECT state
		from tblstates
		where country=<cfqueryparam value="#ucase(arguments.country)#" cfsqltype="cf_sql_varchar">
		LIMIT 1
		</cfquery>
 
		<cfif data.recordcount>
			<cfreturn true>
		<cfelse>
			<cfreturn false>
		</cfif>
	</cffunction>

Ok, not too bad. Now, let’s bind our page to the ajax cfc so we can use this function. Stick this baby at the top of everything on the page:

   <cfajaxproxy cfc="ajax" jsclassname="respond">

Now we need to write the javascript to make the magic happen.

  function showwhich(){
	  var r= new respond();
	  sforc=document.form1.country.value;
	  toshow= r.arestates(sforc);
	  if (toshow == true) 
	  	{
	  	//unhide dropdown, hide text box.
		document.getElementById("dd_state").style.display='inline',
		document.getElementById("type_state").style.display='none',
		document.form1.stateprovt.value=""
		}
	  else {
		//hide dropdown, unhide text box.
		document.getElementById("type_state").style.display='inline',
		document.getElementById("dd_state").style.display='none',
		document.form1.stateprovt.value=""
	  }
 
}

You’ll notice I’ve added a line to clear the value of the text field on each change. This is just idiot-proofing so you don’t have someone come in and enter text, then decide they really did live in the US after all. I have further idiot proofed by adding this code before my form processes:

<cfif isdefined("form.stateprov") and TRIM(form.stateprov) neq ''>
	<cfset use_state=form.stateprov>
<cfelseif isdefined("form.stateprovt") and TRIM(form.stateprovt) neq ''>
	<cfset use_state=form.stateprovt>
<cfelse>
 	<cfset runadd="no">
	<cfset message=message &"<br />The State/Province you entered is invalid.">
</cfif>

Again, I’m sure there are “prettier” ways to accomplish this, but this worked for me!