O'Reilly

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!

Track.js Error Reporting

Upcoming Events

Recent Features

  • 7 Essential JavaScript Functions

    I remember the early days of JavaScript where you needed a simple function for just about everything because the browser vendors implemented features differently, and not just edge features, basic features, like addEventListener and attachEvent.  Times have changed but there are still a few functions each developer should...

  • 5 Ways that CSS and JavaScript Interact That You May Not Know About

    CSS and JavaScript:  the lines seemingly get blurred by each browser release.  They have always done a very different job but in the end they are both front-end technologies so they need do need to work closely.  We have our .js files and our .css, but...

Incredible Demos

  • Digg-Style Dynamic Share Widget Using MooTools

    I've always seen Digg as a very progressive website. Digg uses experimental, ajaxified methods for comments and mission-critical functions. One nice touch Digg has added to their website is their hover share widget. Here's how to implement that functionality on your site...

  • Create a Dynamic Table of Contents Using MooTools 1.2

    You've probably noticed that I shy away from writing really long articles. Here are a few reasons why: Most site visitors are coming from Google and just want a straight to the point, bail-me-out ASAP answer to a question. I've noticed that I have a hard time...

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!

Recently on David Walsh Blog

  • OâReilly Velocity Conference â New York

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • Free Download: Font Bundle Featuring 17 Incredible Typefaces

    The only thing we love more than a good font, is a good free font. So we’ve combed the Web for some of our favorite free fonts, and gathered them here in a single download. You’ll find a variety of useful typefaces, from highly geometric designs...

  • OâReilly Velocity Conference â Amsterdam

    My favorite front-end conference has always been O'Reilly's Velocity Conference because the conference series has focused on one of the most undervalued parts of client side coding:  speed.  So often we're so excited that our JavaScript works that we forget that speed, efficiency, and performance are just as important. The next Velocity...

  • CanIUse Command Line

    Every front-end developer should be well acquainted with CanIUse, the website that lets you view browser support for browser features.  When people criticize my blog posts for not detailing browser support for features within the post, I tell them to check CanIUse:  always up to date, unlike...

  • Generating Alternative Stylesheets for Browsers Without @media

    If your CSS code is built with a mobile-first approach, it probably contains all the rules that make up the "desktop" view inside @media statements. That's great, but browsers that don't support media queries (IE 8 and below) will simply ignore them, ending up getting the...