Simple Animation Demo:

Hover over the active arrow tabs to scroll the items in the list:


The Code:

/**
 * add a function to an event, attached to a particular object
 * @param {object}		obj		the object that will trigger the event
 * @param {string}		type	the name of the event
 * @param {function}	fn		the function to attach
 **/
function addEvent( obj, type, fn ) {
    if (obj.addEventListener) {
        obj.addEventListener( type, fn, false );
    }
    else if (obj.attachEvent) {
        obj["e"+type+fn] = fn;
        obj[type+fn] = function() { obj["e"+type+fn]( window.event ); };
        obj.attachEvent( "on"+type, obj[type+fn] );
    }
    else {
        obj["on"+type] = obj["e"+type+fn];
    }
}

/**
 * Creates a delegate (callback) that sets the scope to obj.
 * Call directly on any function. Example: this.myFunction.createDelegate(this)
 * Will create a function that is automatically scoped to this.
 * @param {object}		obj (optional)			The object for which the scope is set
 * @param {array} 		args (optional)			Overrides arguments for the call. 
 * 												(Defaults to the arguments passed by the caller)
 * @param {bool/Number}	appendArgs (optional)	if True args are appended to call args instead of overriding,
 * 												if a number the args are inserted at the specified position
 * @return {function} The new function
 */
Function.prototype.createDelegate = function(obj, args, appendArgs){
	var method = this;
	return function() {
		var callArgs = args || arguments;
		if(appendArgs === true){
			callArgs = Array.prototype.slice.call(arguments, 0);
			callArgs = callArgs.concat(args);
		}else if(typeof appendArgs == "number"){
			callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
			var applyArgs = [appendArgs, 0].concat(args); // create method call params
			Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
		}
		return method.apply(obj || window, callArgs);
	};
};

/** 
 * left-pad a number or a string with a given character so that 
 * it's at least the given number of characters long 
 * @param 	{mixed}		val		the string or number to pad
 * @param 	{int}		len		the desired length
 * @param 	{string}	pad		the character to use for padding
 * @return 	{string}	out		the newly formatted value as a string
 **/  
function leftPad(val, len, pad) {  
    return (new Array(len - String(val).length + 1)).join(pad).concat(val);  
}  

/** 
 * create a scrolling navigation pane
 * @requires addEvent(obj,type,fn)
 * @requires leftPad(val,len,pad)
 * @requires Function.prototype.createDelegate(obj, args, appendArgs)
 **/ 
var NavPane = {
	px: 1, 		// number of pixels that the pane will move in each scrolling interval
	rate: 5, 	// number of milliseconds between each position adjustment
	boxes: 10,	// TODO: get the content info and number of boxes from an API or page load data
	intvl: [],	// holder for the motion intervals
	axis: { 	// enumeration for directions
		right: 0,
		left: 1
	},
	events: { // setup low level event tracking (normally would create events using a 
		rActive: false,
		lActive: false,
		rEnabled: true,
		lEnabled: false
	},
	/** 
	 * render the NavPane
	 * create DOM elements to represent panel boxes 
	 **/ 
	init: function() {
		// gather pane skeleton (TODO: we could create this dynamically in this class)
		this.navLeft = document.getElementById('navLeft');
		this.navRight = document.getElementById('navRight');
		this.navPaneWrap = document.getElementById('navPaneWrap');
		this.navPane = document.getElementById('navPane');
		
		// create pane
		this.render();
		
		// add events
		addEvent(this.navLeft,'mouseover', this.setScrollInterval.createDelegate(this,[this.axis.left]));
		addEvent(this.navRight,'mouseover', this.setScrollInterval.createDelegate(this,[this.axis.right]));
		
		addEvent(this.navLeft,'mouseout',this.stop.createDelegate(this,[this.axis.left]));
		addEvent(this.navRight,'mouseout',this.stop.createDelegate(this,[this.axis.right]));
	},
	/** 
	 * render the NavPane
	 * create DOM elements to represent panel boxes 
	 **/ 
	render: function() { 
		var w = this.boxes*71;
		this.navPane.style.width = w+'px'; // TODO: fetch the width of the boxes dynamically or define in JS
			
		for(var i=1;i<=this.boxes;i++) {
			var el = document.createElement('div');
			el.innerHTML = 'box '+leftPad(i,3,'0');
			this.navPane.appendChild(el);
		}
			
		this.minLeft = -(w - this.navPaneWrap.offsetWidth);
		this.maxLeft = 0;
	},
	setScrollInterval: function(dir){
		this.intvl[dir] = setInterval(this.scroll.createDelegate(this,[dir]),this.rate);
	},
	scroll: function(dir) {
		var xpos = parseInt(this.navPane.offsetLeft || 0);
		if(dir==this.axis.right) {
			if(xpos > this.minLeft) {
				xpos -= this.px;
				this.navPane.style.left = xpos + 'px'; // move right
				this.navLeft.className = "navBtn";
			}else{ // stop it
				this.stop(dir);
				this.navRight.className = "navBtn inactive"; // turn inactive
			}		
		}else{
			if(xpos < this.maxLeft) {
				xpos += this.px;
				this.navPane.style.left = xpos + 'px'; // move left
				this.navRight.className = "navBtn";
			}else{ // stop it
				this.stop(dir);
				this.navLeft.className = "navBtn inactive"; // turn inactive
			}
		}
		// if we want the menu to speed up more the longer we hover
		// TODO: need some special handling
		//this.intCount++;
		//if(!(this.intCount%100))	this.px++;
	},
	stop: function(dir){
		if(this.intvl[dir]) {
			clearInterval(this.intvl[dir]);
			this.intvl[dir] = 0;
			//this.px = 1;
		}
	}
};

addEvent(window,'load',function() {
	NavPane.init();
});