Archive

Archive for the ‘javascript’ Category

Release: infinite-scroll.com! (w/ jquery and wordpress plugins)

September 25th, 2008

Today I'm releasing www.infinite-scroll.com. Essentially infinite scroll is using ajax to pre-fetch content from a subsequent page and add it directly to the user’s current page. If you have no idea what I'm talking about, you should scroll down to the bottom of aurgasm.us, roflcon.org, or blog.molecular.com.

There is a full design pattern explanation describing when to use it, and considerations to think about when implementing. I also wrote a history of infinite scroll, to cover the history of this somewhat new ajax-enabled interaction technique.

But most importantly, there are two software releases in here:

The Infinite Scroll WordPress plugin

Enable your wordpress blog with infinite scroll functionality without knowing any javascript. You just need to know css selectors. And no theme php changes should be required to use.

The Infinite Scroll jQuery plugin

Enable anything! Obviously this is for javascript developers only, but it should give you enough flexibility to utilize in your own application.

Check it all out at www.infinite-scroll.com.





Since this is a development-focused blog, you may ask…

How is it done?

There is a little known feature in the .load() method that lets you specify the CSS selector of the html you want to include. jQuery will load in any local URL, then parse the html and grab only the elements you've defined with your selector. This allows for some pretty fun shit: client-side transclusions (a la purple include) ; and some really kickass shit when you combo it with a local php proxy.

This is really the meat of the code:

// load all post divs from page 2 into an off-DOM div
$('<div/>').load('/page/2/ #content div.post',function(){ 
    $(this).appendTo('#content');    // once they're loaded, append them to our content area
});

So the infinite scroll plugin basically leverages that load() method at its core. It's basically scraping your existing page structure, which means you don't need to code any custom backend stuff to enable this functionality! Booyah, right?

The rest of the code maintains the status of the ajax call, dies when it finishes going through your pages, and show/hides the loading notification. Please take a look and let me know what you think!

front-end development, javascript, jquery

Sequentially chain your callbacks in jQuery – Three ways

August 22nd, 2008

I had a challenge to get four divs to fade in sequentially.

Writing this out the long way is not really my favorite:

$("#vehicle1").fadeIn("slow", function(){
  $("#vehicle2").fadeIn("slow", function(){
    $("#vehicle3").fadeIn("slow", function(){
      $("#vehicle4").fadeIn("slow");
    });
  });
});

Ewwww. right?

After some conversation in the #jQuery IRC channel, I present twothree classier ways of accomplishing the same thing:

Self-executing callback chaining

(function showVehicle(elem){
  elem.fadeIn('slow',function(){
    $(this).next().length && showVehicle($(this).next()); 
  });
})( $("div.vehicle:first") );

Custom event triggering (via: ajpiano)

$('div.vehicle')
  .bind('showVehicle',function(e) {$(this).fadeIn('slow',function(){ 
    $(this).next().length && $(this).next().trigger("showVehicle"); 
  })})
  .eq(0)
    .trigger('showVehicle');
Update 2009.06.15: I'm incredibly impressed with temp01's solution in the comments. So that's my recommendation going forward:

Self-executing callback chain on an arbitrary jQuery object (via: temp01)

(function hidenext(jq){
    jq.eq(0).fadeOut("fast", function(){
        (jq=jq.slice(1)).length && hidenext(jq);
    });
})($('div#bodyContent a'))

javascript, jquery

Best practice: Poll instead of a setTimeout hack

May 13th, 2008

Very often you'll have events happening asynchronously, but you need to wait until one has completed before you fire the second.
And you may not have the ability of attaching a callback function to the first.

In my less wise days I'd say "Lets just setTimeout it for a couple seconds…" but always felt really dirty about it.

A classier approach I've used lately is to poll for a change. Here I'm using the gmail greasemonkey API and waiting for it to load in before I start using it:

gmonkey.load("1.0");
 
// this is a self-executing anonymous function that uses setTimeout to call itself 
// at 50ms intervals until the isLoaded variable resolves as true.
 
(function(){
  if (gmonkey.isLoaded){
    // do stuff i want to do with the API
    gmonkey.get('1.0').addNavModule('notepad', '<iframe src="http://aaronboodman.com/halfnote/"></iframe>');
  } else {
    setTimeout(arguments.callee,50);
  }
})();

I found myself doing this a lot when loading in multiple external resources and playing with them.. So to generalize the code I wrote executeWhenLoaded():

// executeWhenLoaded() will be overloaded with as many arguments as i want to check for presence.
function executeWhenLoaded(func){  
 
  for (var i = 1; i<arguments.length; i++){ // for loop starts at 1 to skip the function argument.
    if (! window[ arguments[i] ]) {
      setTimeout(arguments.callee,50);      
      return;
    } 
  }
 
  func(); // only reaches here when for loop is satisfied.
}
 
// and in use:
 
executeWhenLoaded(function(){ 
    console.log(session.data);
},'session');   // session will return a value when the whatever preceding functionality is done.

executeWhenLoaded's first argument is the function to call, it can an unlimited number of arguments, which are all strings that reflect objects in the global namespace that have to be present in order to execute that function.

Update: In the comments, ProggerPete notes that this is not cross-browser compatible.. yet! In IE6, at least, the browser loses the original reference to the arguments object when it cycles through on the arguments.callee call. He offers a fix below.

front-end development, javascript

Quick internationalized sort in javascript

April 21st, 2008

Let's take a list of countries that was originally alphabetized in English, but is now translated to French.

var arr = ["Argentine", "Australie", "Autriche", "Belgique", "Brésil", "Canada", "Chili", 
"Chine", "Costa Rica ", "République Tchèque", "Danemark", "Équateur", "El Salvador ", 
"Finlande", "France", "Allemagne", "Guatemala", "Hong Kong", "Hongrie", "Inde", "Irlande", 
"Italie", "Japon", "Corée du Sud", "Luxembourg", "Mexique", "Pays-Bas", "Nouvelle-Zélande", 
"Norvège", "Panama", "Pologne", "Portugal", "Russie", "Slovaquie", "Espagne", 
"la Suède", "Suisse", "Turquie", "Royaume-Uni", "Uruguay", "États-Unis"]

You can see the incorrect sort order for Germany ("Allemagne") and the US ("États-Unis").
Running the standard javascript Array.sort() will sort it according to the American English language:

arr.sort();
/*==>
["Allemagne", "Argentine", "Australie", "Autriche", "Belgique", "Brésil", "Canada", "Chili", 
"Chine", "Corée du Sud", "Costa Rica ", "Danemark", "El Salvador ", "Espagne", "Finlande", 
"France", "Guatemala", "Hong Kong", "Hongrie", "Inde", "Irlande", "Italie", "Japon", 
"Luxembourg", "Mexique", "Norvège", "Nouvelle-Zélande", "Panama", "Pays-Bas", "Pologne", 
"Portugal", "Royaume-Uni", "Russie", "République Tchèque", "Slovaquie", "Suisse", "Turquie",
 "Uruguay", "la Suède", "Équateur", "États-Unis"] */

Note the misplacement of the last three entries. A real internationalized sort of this would be a huge motherbitch to implement, but here is a quick and hacky way to get your ducks in order:

  arr.sort(function(a,b){
 
    function normalize(str){
       return str
               .toLowerCase()
               .replace(/è|é|ê|ë/,'e').replace(/ò|ó|ô|õ|ö/,'o').replace(/ì|í|î|ï/,'i')
               .replace(/à|á|â|ã|ä|å|æ/,'a').replace(/ù|ú|û|ü/,'u');
    }
 
    a = normalize(a);
    b = normalize(b);
 
    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
  });
/*==>
["Allemagne", "Argentine", "Australie", "Autriche", "Belgique", "Brésil", "Canada", "Chili", 
"Chine", "Corée du Sud", "Costa Rica ", "Danemark", "El Salvador ", "Équateur", "Espagne",
 "États-Unis", "Finlande", "France", "Guatemala", "Hong Kong", "Hongrie", "Inde", "Irlande", 
"Italie", "Japon", "la Suède", "Luxembourg", "Mexique", "Norvège", "Nouvelle-Zélande", 
"Panama", "Pays-Bas", "Pologne", "Portugal", "République Tchèque", "Royaume-Uni", 
"Russie", "Slovaquie", "Suisse", "Turquie", "Uruguay"] */

It's not perfect (I bet that "la Suède" should actually be in the S's), but it'll get you a bit closer without too much effort.

2009.10.29: A much better method:
  arr.sort(function(a, b) {
    if (typeof a === 'string' && typeof b === 'string') {
      return a.toLowerCase().localeCompare(b.toLowerCase());
    });

hacks, javascript

How to keep friend requests out of your inbox

April 1st, 2008

If you're incredibly popular on the internet, like me, then I can hear you crying for help. :)

Friend requests littering your inbox can get annoying. They certainly don't need your attention right now, so why not through them to a more passive information consumption area: RSS. That way you can process them en masse, when you're ready to.

First, we use the filters in Gmail to identify all friend request emails.

  • Set up a new filter.
  • In the subject area put in this text:
    {"friend request" "is now following you" "newest contact" "friends on yelp" "added you as a" "has requested your trust" "wants to be your friend" "invited you to connect" "would like to be added"}
  • Click next, then select Skip the Inbox
  • Check Forward it to and in the box put in a unique-email-address @ mailbucket.org. (For example: paulsfriendrequests@mailbucket.org) This will be a public feed, so… yeah.
  • Click Create Filter

friendrequestfilter.PNG

Your newly created RSS feed will be at http://mailbucket.org/unique-email-address.xml.

This filter will catch all friend requests from: Myspace, Facebook, LinkedIn, Flickr, Spokeo, Twitter, Yelp, and Plaxo Pulse. (And should be pretty trivial to add new ones. :)

hacks, javascript