Create a Twitter AJAX Button with MooTools, jQuery, or Dojo

By  on  
Twitter Button

There's nothing like a subtle, slick website widget that effectively uses CSS and JavaScript to enhance the user experience.  Of course widgets like that take many hours to perfect, but it doesn't take long for that effort to be rewarded with above-average user retention and buzz.  One of the widgets I love is Twitter's "Follow" button.  Let me show you how you can implement this functionality with three popular JavaScript toolkits:  MooTools, jQuery, and Dojo.

Note:  This tutorial will only display the client side handling of the form submission -- NOT any PHP/MySQL/server-side handling of the request.

The HTML Structure

<form class="follow-form" method="post" action="twitter-follow.php">
	<input type="hidden" name="followID" value="123456" />
	<button type="submit" value="Actions" class="btn follow" title="123456">
		<i></i><span>follow</span>
	</button>
</form>

The HTML for the button is very simple.  The structure revolves around a BUTTON element which contains an I element and SPAN element.  You're probably thinking "An I element?  WTF."  I know I did.  The truth of the matter is that the I element is deprecated and, as far as I'm concerned, and be used for any purpose the developer would like.  I'm also sure that Twitter doesn't mind saving bytes here or there.

The CSS Styles

/* twitter button and its children */
button.btn { 
	-moz-border-radius:4px;
	-webkit-border-radius:4px;
	background-attachment:scroll;
	background-color:#ddd;
	background-image:url(http://s.twimg.com/a/1282150308/images/buttons/bg-btn.gif);
	background-position:0 0;
	background-repeat:repeat-x;
	border:1px solid #ddd;
	border-bottom:1px solid #ccc;
	color:#333;
	cursor:pointer;
	font-family:"Lucida Grande",sans-serif;
	font-size:11px;
	line-height:14px;
	padding:4px 8px 5px 8px;
	text-shadow:1px 1px 0 #fff;
	vertical-align:top;
}
button.btn:hover {
	border:1px solid #999;
	border-bottom-color:#888;
	color:#000;
	background-color:#d5d5d5;
	background-position:0 -6px;
}
button.btn:active {
	background-image:none !important;
	text-shadow:none !important;
}
			
button.btn i {
	background-image:url(http://s.twimg.com/a/1282150308/images/sprite-icons.png);
	background-position:-176px -32px;
	background-repeat:no-repeat;
	display:inline-block;
	height:13px;
	margin-right:5px;
	width:15px;
}
button.btn i.active	{ background:url(http://s.twimg.com/a/1282150308/images/spinner.gif); }

/* properties for the element that is generated *after* following */
span.following	{  background:#ffd; padding:5px 10px; }
span.following span { width:10px; height:9px; margin-right:5px; display:inline-block; background:url("http://s.twimg.com/a/1282150308/images/sprite-icons.png") -160px -16px no-repeat; }

Most of the styling for this button goes onto the BUTTON element itself.  You'll notice directives to round the button;  leaving the button sharp also please the eye.  Through the regular, hover, and active button states, check out how Twitter users the background position and colors to nicely modify the button without the need for additional images.  You'll also notice Twitter uses sprites...as should you.

The MooTools JavaScript

/* with mootools */
window.addEvent('domready',function() {
	/* fetch elements */
	$$('form.follow-form').each(function(form) {
		/* stop form event */
		form.addEvent('submit',function(e) {
			/* stop event */
			if(e) e.stop();
			/* send ajax request */
			var i = form.getElement('i');
			new Request({
				url: 'twitter-follow.php',
				method: 'post',
				data: {
					followID: form.getElement('input').value
				},
				onRequest: function() {
					i.addClass('active');
				},
				onSuccess: function() {
					var button = form.getElement('button');
					button.setStyle('display','none');
					new Element('span',{
						html: '<span></span>Following!',
						'class': 'following'
					}).inject(button,'after');
				},
				onComplete: function() {
					i.removeClass('active');
				}
			}).send();
		});
	});
});

The first step is grabbing all of the FORM elements with the follow-form CSS class.  Upon form submission, the default submission action is stopped.  An AJAX request is fired, using the INPUT element's ID as the user to follow.  When the request is fired, the I element's background image is set to the spinner.  When the request is complete, the button is hidden and a new SPAN element is displayed informing the user that they are now following the given user!

The jQuery JavaScript

// Idiomatic jQuery by Doug Neiner
jQuery(function ($) {
	/* fetch elements and stop form event */
	$("form.follow-form").submit(function (e) {
		/* stop event */
		e.preventDefault();
		/* "on request" */
		$(this).find('i').addClass('active');
		/* send ajax request */
		$.post('twitter-follow.php', {
			followID: $(this).find('input').val()
		}, function () {
			/* find and hide button, create element */
			$(e.currentTarget)
			  .find('button').hide()
			  .after('&lt;span class="following"&gt;&lt;span&gt;&lt;/span&gt;Following!&lt;/span&gt;');
		});
	});
});

The code above is based off of the MooTools code.  The workflow is exactly the same.

The Dojo JavaScript

/* when the DOM is ready */
dojo.ready(function() {
	/* fetch elements */
	dojo.query('form.follow-form').forEach(function(form) {
		/* stop form event */
		dojo.connect(form,'onsubmit',function(e) {
			/* stop event */
			dojo.stopEvent(e);
			/* active class */
			dojo.query('i',form).addClass('active');
			/* get button */
			var button = dojo.query('button',form)[0];
			/* ajax request */
			dojo.xhrPost({
				form: form,
				load: function() {
					dojo.style(button,'display','none');
					dojo.create('span',{
						innerHTML: '<span></span>Following',
						className: 'following'
					},button,'after');
				}
			});
		});
	});
});

The code above is based off of the MooTools code.  The workflow is exactly the same.

This "Follow" button is only one of many details that Twitter has paid attention to, just to make the user experience on the site better.  Take note from the effort put forth by large websites -- adding these types of details to your smaller websites can make the user experience much better for YOUR users!

Recent Features

  • By
    fetch API

    One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to...

  • By
    Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API...

Incredible Demos

Discussion

  1. In each of the jQuery(this).find(sel) cases, you can make things a bit more efficient by using the secondary context argument. Looks a lot like the dojo.query form.

    Before: jQuery(this).find('i')
    After: jQuery('i', this)

    Should be a good deal more performant.

    Some people prefer the .find approach – but in that case, I’d suggest keeping a copy of the first call to jQuery(this) around, so it doesn’t have to recreate that object three times – something one commonly sees will be things like var $this = jQuery(this); and then you could just kick off $this.find – not the best efficiency gain ever, but every bit counts. :D

    Or, if you’re really going to be doing jQuery(this) a lot of times and working primarily with the jQueryfied element in the loop, check out Ben Alman’s each2 plugin – http://benalman.com/projects/jquery-misc-plugins/#each2 – there’s some serious performance gains using that approach. I was stunned. Good stuff.

    • Hey Brian, if you check the jQuery source, jQuery(this).find("i") would be a tad more performant since jQuery just calls that exact format internally if you do it the other way you suggested with context. (Reference: http://github.com/jquery/jquery/blob/master/src/core.js#L150-154). However, Ben Almans plugin is a great plugin if you use each a lot. In the code in this post, $.each was unnecessary as the bind method does an implicit each when binding.

    • Hey David! We need to catch up, hope you are doing well!

      I wonder if you write your jQuery to look like MooTools code on purpose, or it happens by accident :) I took a stab at writing it in a more idiomatic jQuery fashion using some optimizations and short form where possible. The the most important optimization is that you don’t need to call .each on the form selector as .bind does an implicit each on it anyway. A second readability optimization is that you can pass $ as the first parameter of the anonymous document ready callback function and use it internally instead of the more verbose jQuery term. You can check out the original and the revised as a Gist.”>http://gist.github.com/550834″ rel=”nofollow”>the original and the revised as a Gist.

      the original and the revised as a Gist.”>
    • I’m probably guilty of writing jQuery MooTools-style. I’ve updated my post to use your more awesome version.

  2. Nice article. The only thing is that I couldn’t figure out the reason behind using .each() and attaching events one by one. This could also be done like $(selector).bind('someAction',fn() {doSomething;}); (It basically does the same thing but the code gets simpler)

  3. Is there a way that I can use this with the twitter api? I mean, if the one who click the button is loged on twitter he will start to follow me, otherwise a popup to login open up, all with no server side from my domain (just twitter’s).

  4. The Dojo example could be simplified a bit by doing this:

    dojo.ready(function(){
        dojo.query('form.follow-form').onsubmit(function(e){
            dojo.stopEvent(e);
            dojo.query('i', this).addClass('active');
            // ...
        });
    });
    

    Almost all DOM events can be bound like that and it will run the function in the context of the node (this will be the node).

  5. Hey David,

    I also have a simplification for the MooTools code.

    $$('form.follow-form').each(function(form) { 
        /* stop form event */ 
        form.addEvent('submit',function(e) { 
          // [...]
        }
      });
    

    Shouldn’t be this code be written as follow:

    $$('form.follow-form').addEvent('submit',function(e) { 
          // [...]
      });
    
  6. Russell

    Love this great code, but I want to know something a bit different.
    When you are on Twitter.com and you have some new post, what I would like to do, is allow new posts to load, the same way that twitter allow.

    it seems that Twitter inserts data in between divs depending on the users post time.

    I am wondering how do we do that with jQuery?

  7. suliaman

    hello i would really like to know how to deal with twitter-follow.php, i know how to the mysql side of things, but how i send back a success when the data has been entered into the database, thank you very much

  8. David,
    I like the way you did it.
    well, to be honest, I am a nut when it comes to coding.
    But, I’d like to add a checkbox after my blog’s comment field. When people check that box while commenting, then automatically follow me on twitter.

    Is that possible?
    Thanks
    Abhik

  9. bose

    Here’s my attempt at writing Mootools in jQuery style, not sure if it works?

    /* with mootools */
    window.addEvent('domready',function() {
        /* fetch elements and stop form event*/
        $$('form.follow-form').addEvent('submit',function(e) {
            /* stop event */
            if(e) e.stop();
            /* send ajax request */
            this.getElement('i').addClass('active');
            new Request({
                url: 'twitter-follow.php',
                onSuccess: function() {
                    /* find and hide button, create element */
                    var button = e.target.getElement('button').hide();
                    new Element('span.following',{
                        html: 'Following!'
                    }).inject(button,'after');
                }
            }).post({followID: e.target.getElement('input').value});
        });
    });
    
  10. Great! THX from Sarajevo!

  11. Steve

    Great code but missing just as important “un-follow functionality”…

  12. Atticus

    This is very cool! How would you implement an “un-follow” button in the same space, once the button has changed to “following!” ?

  13. Call me a n00b, but I’m stuck at the PHP part. Where is the code for twitter-follow.php? Is that in a separate article? As far as I can tell, when I click on this button, it tries to reference that page, but I don’t have it yet. It’s been a while since I did anything with AJAX, so this could just be a brain fart… Would you mind pointing me in the right direction?

    I’m frustrated that Twitter’s @Anywhere API doesn’t define a SIMPLE follow button that ONLY has the user follow the specified Twitter account, and that’s IT. I don’t want my users to have to install a custom app on their Twitter account. I won’t follow anyone who tries to make me give up information about myself just to follow them, and I expect my website visitors feel the same way. Facebook has a VERY simple “Like” button that works on any website without any hassle. Why all the nonsense, Twitter?

  14. ram

    Can u give the php code?

  15. neha sachan

    sir,i m trying your script but its not working,m using html,css and jquery.

  16. Very good script. For me, working perfectly, but I touched a little code : )

  17. It will be nice to have a sample of what the twitter-follow.php would look like. Am trying to adapt parts of this tutorial to a project am working on (non-twitter related)

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!