Archive

Archive for June, 2009

Random hex color code generator in JavaScript

June 19th, 2009

For fun I asked a few friends for ideas on a random color generator in a single line of javascript. You know, these guys: #0afec0, #c9f2d0, #9b923e.

Here's what we came up in about two minutes (in chronological order)…

'#' + (function co(lor){   return (lor += 
  [0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f'][Math.floor(Math.random()*16)]) 
  && (lor.length == 6) ?  lor : co(lor); })('');

Similar recursive technique, but using a string instead of an array and aliasing the Math object:

(function(m,s,c){return (c ? arguments.callee(m,s,c-1) : '#') + 
  s[m.floor(m.random() * s.length)]})(Math,'0123456789ABCDEF',5)

Using a named function expression instead of arguments.callee.

'#'+(function lol(m,s,c){return s[m.floor(m.random() * s.length)] +
  (c && lol(m,s,c-1));})(Math,'0123456789ABCDEF',4)

If we assume JavaScript 1.6, then we could just use Array.map():

'#'+'0123456789abcdef'.split('').map(function(v,i,a){
  return i>5 ? null : a[Math.floor(Math.random()*16)] }).join('');

But then, the magic of Math struck (16777215 == ffffff in decimal):

'#'+Math.floor(Math.random()*16777215).toString(16);
Thx to ben alman, nlogax, and temp01 for their smarts.

Paul Irish javascript

log() - A lightweight wrapper for console.log

June 9th, 2009

There are a few things that a console.log wrapper can and should do:

  • Prevent errors if a console isn't around (i.e. IE)
  • Maintain a history of logs, so you can look in the past if your console is added afterwards (e.g. firebug lite)
  • Normalize the browser differences in console integration (e.g. when passing multiple arguments into console.log())
  • For something you type regularly, make it quicker to type for the lazy among us.

But there are a few considerations…

Console.log.apply doesn't handle multiple arguments in Safari 3 or Chrome 1.1

Firebug, Chrome, and Safari have a clearer presentation for strings when inside an array:

However while the improved array-wrapped presentation is nice, nested arrays get truncated:

Truncation is no good, so here we avoid array-wrapping when we can.

Extra features

  • A reverse-chronological history, accessible as an array at log.history
  • I have also included a shorthand logargs() function that is useful when you're inside a function and want to know the context and arguments passed in. I use it a lot in ajax callbacks. Worth noting that it uses arguments.callee.caller, which will be deprecated in ECMAScript 5 Strict mode. :(

The code:

 
window.log = function(){
 
  // store logs to an array for reference
  log.history = log.history || [];
  log.history.push(arguments);
 
  window.console && console.log[console.firebug ? 
    'apply' : 'call'](console,Array.prototype.slice.call(arguments));
}
 
// logargs(this); == console.log(this,arguments);
window.logargs = function(context){
  // grab the calling functions arguments
  log(context,arguments.callee.caller.arguments); 
}

And if you'd like it minified:

window.log=function(){var a="history";log[a]=log[a]||[];log[a].push(arguments);window.console&&console.log[console.firebug?"apply":"call"](console,Array.prototype.slice.call(arguments))};window.logargs=function(a){log(a,arguments.callee.caller.arguments)};

Interestingly, the minified version of this script is smaller (262 bytes), and arguably more useful, than the minified firebugx.js, which I've covered before. Plus it has quite a few more features.

Demo?

log() and logargs() demo

Plus Firebug lite?

You got it. This bookmarklet will add firebug lite, and then output the logged history when it's ready:
>>> Fbug Lite+log <<<

Want more power?

After writing this, I worked with Ben Alman on a more comprehensive and robust logging script. It's excellent if you take full advantage of the console API. And you should be aware that Safari 4 and Chrome 2 have most of that API supported. Make full use of it and don't you dare type another alert()!

Paul Irish javascript

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.

Paul Irish jquery