Archive

Archive for the ‘jquery’ Category

jQuery idleTimer plugin

June 3rd, 2009

There are a few cases where you want to know if the user is idle. Namely:

  • You want to preload more assets
  • You want to grab their attention to pull them back
  • You want close their banking session after 5 minutes of inactivity. (Jerk!)
  • You want the site to sneak off the screen and see if they notice ;-)

Nick Zakas wrote a script for YUI3 to handle these cases. His writeup has a great description of the architecture approach he took to the script.

In my jQuery adaptation, I did a few different things:

  1. Leveraged event namespaces for easy unbinding
  2. Considered mousewheel as activity, in addition to keyboard and mouse movement.
  3. Gave it a bit more jQuery-ish of an API

To use:

// idleTimer() takes an optional argument that defines the idle timeout
// timeout is in milliseconds; defaults to 30000
$.idleTimer(10000);
 
 
$(document).bind("idle.idleTimer", function(){
 // function you want to fire when the user goes idle
});
 
 
$(document).bind("active.idleTimer", function(){
 // function you want to fire when the user becomes active again
});
 
// pass the string 'destroy' to stop the timer
$.idleTimer('destroy');

Get the source on github (4.6k unminified)
View the demo

Note: If you want to change the timeout interval, you'll have to destroy the existing timer first.

2009.09.22: I've updated the idleTimer script for a few more features…
// you can query if the user is idle or not with data()
$.data(document,'idleTimer');  // 'idle'  or 'active'
 
// you can get time elapsed since user when idle/active
$.idleTimer('getElapsedTime'); // time since state change in ms

You can get the latest code, naturally, on github.

2010.05.11: Due to popular demand, there is now support for multiple timers!!

Why would you want this? Lets say you want ..

  • One Timer to restore the forms, messages boxes, etc.

  • Another Timer of a different length timeout to notify about expiration sessions.
// bind to specific elements, allows for multiple timer instances
$(elem).idleTimer(30*1000);
$(elem).idleTimer('destroy');
$(elem).idleTimer('getElapsedTime'); // some ms something
 
$.data(elem,'idleTimer');  // 'idle'  or 'active'

Notice this new API is on $.fn.idleTimer, as in it works on a jQuery collection instead of just the jQuery global object.

If you're using this along with the old $.idleTimer api, you should not do $(document).idleTimer(...)

All these element-bound timers will only watch for events inside of them. You may just want to watch page-level activity, in which case you may set up your timers on document, document.documentElement, and document.body. Those will allow three separate timers that will catch all page activity.

Again, check out the demo or view the source on github. :)

2011.03.11 : You should check out Eric Hynds' Idle Timeout plugin. It improves a good bit on this guy here.

jquery

Flip text upside-down jQuery plugin

April 2nd, 2009

For April Fools day, Youtube enabled you to view any page and video, totally upside down.

Pretty cute and clever, so I unapologetically stole their code and re-appropriated it as a jQuery plugin. As you can see, I have very valuable time. :-p

Download: jquery.textflip.js 1.4K

Demo: Click me to flip all this posts text

jquery

Markup-based unobtrusive comprehensive DOM-ready execution

March 11th, 2009
2012.02.07: Looking back at this very awkward name is.. awkward. This technique is called routing and is available in Rails, Django, Sammy.js, LeviRoutes and Backbone. I'd essentially rename this technique DOM-based Routing, with the catch that it's for page refresh situations and not single page apps.

On a recent project I took my previous approach to automating firing of onload events to a new level.

For instance if your code was architected in an object literal such as:

 
FOO = {
  common : {
    init     : function(){ ... },
    finalize : function(){ ... }
  },
  shopping : {
    init     : function(){ ... },
    cart     : function(){ ... },
    category : function(){ ... }
  }
}

A page with this body tag:

<body id="cart" class="shopping">

would load these functions sequentially:

UTIL.fire is calling: FOO.common.init()
UTIL.fire is calling: FOO.shopping.init()
UTIL.fire is calling: FOO.shopping.cart()
UTIL.fire is calling: FOO.common.finalize()

In addition, using these classes and IDs on the body tag provides some excellent specific hooks for your CSS.

The javascript:

UTIL = {
 
  fire : function(func,funcname, args){
 
    var namespace = FOO;  // indicate your obj literal namespace here
 
    funcname = (funcname === undefined) ? 'init' : funcname;
    if (func !== '' && namespace[func] && typeof namespace[func][funcname] == 'function'){
      namespace[func][funcname](args);
    } 
 
  }, 
 
  loadEvents : function(){
 
    var bodyId = document.body.id;
 
    // hit up common first.
    UTIL.fire('common');
 
    // do all the classes too.
    $.each(document.body.className.split(/\s+/),function(i,classnm){
      UTIL.fire(classnm);
      UTIL.fire(classnm,bodyId);
    });
 
    UTIL.fire('common','finalize');
 
  } 
 
}; 
 
// kick it all off here 
$(document).ready(UTIL.loadEvents);

This system worked very well and keeps you in serious control of the execution order.

In the end, I used this plus a custom event to bind super low priority script.
For example:

$(document).bind('finalized',function(){ ... }); // placed within a FOO.shopping.category()

And I'd trigger that

$(document).trigger('finalized');

at the very end of UTIL.loadEvents(). This allows you to keep similar code together, but delay portions responsibly without any setTimeout ugliness.

2010.10.28: OMG this shit just got next level….

So Blake Waters presented this topic in August this year.

And now Jason has published his approach on the most excellent Viget Inspire blog. Please check it out.

It's essentially using data-attributes instead of classes to trigger this action.. So..

   <body data-controller="users" data-action="show">

Will fire off:

FOO.common.init();
FOO.users.init();
FOO.users.show();

Boom boom bam. I dig it. Check it out in its full glory.

javascript, jquery

Cornify Konami easter egg plugin with jQuery

February 4th, 2009

No doubt you've heard that Cornifying is the latest and greatest invention of the internet.

Now you can surreptitiously add it to your own sites, joining The Underground Cabal of Mirthful Protectors of the Corn:

var kkeys = [], konami = "38,38,40,40,37,39,37,39,66,65";
$(document).keydown(function(e) {
  kkeys.push( e.keyCode );
  if ( kkeys.toString().indexOf( konami ) >= 0 ){
    $(document).unbind('keydown',arguments.callee);
    $.getScript('http://www.cornify.com/js/cornify.js',function(){
      cornify_add();
      $(document).keydown(cornify_add);
    });          
  }
});

After a user hits the Konami code (up,up,down,down,left,right,left,right,b,a), every other keystroke will add happiness to the world.

You'll find this website has already been enabled. Try it out. :)

Update 2009.04.27: It was discovered today ESPN.com was hiding some unicorns under its konami. (kotaku coverage) In fact it had been up there for weeks. :)

2010.05.29: BoingBoing will probably add it. And yeah this feature kinda triggered a meme. :p

2012.03.22: Gunnar Hoffman wrote a better implementation: http://paulirish.com/2009/cornify-easter-egg-with-jquery/#comment-68011

jquery

Give your error pages extra FAIL

January 22nd, 2009

A site I'm currently working has had boring and barren error pages. Until now!

I wanted some images from failblog.org to liven things up a bit. In a quick 20 minute hack, I took the site's rss feed, parsed it for the right content using Yahoo Pipes, exported as JSON-P, pulled that in with jQuery, and displayed a random image.

Here's the pipe I'm using:

The trick to pulling in Yahoo Pipe data via JSON-P is by adding a "_callback" argument, defining the callback function name.

The script:

jQuery.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=0vxHL0Lo3RGc2uL8pQt1Yg&_render=json&_callback=FailImages&callback=?');

function FailImages(data){
  var imgs = data.value.items;
  if (imgs){
    var url = imgs[Math.floor(Math.random()*imgs.length)].url;
    jQuery('< img src="'+url+'">').insertAfter('h1');
  }
}

The result:
2009-01-22_2049.png

And of course..
Demo page

front-end development, jquery