Posted on 16th October 2008 — Making use of the overflow and scrollLeft DOM property to scroll elements is a much more effective use of the CPU, over animating using CSS top/left. So this episode of J4D demonstrates the same effect used in two completely different ways.

The second was a request from Trevor Morris who’s involved with/runs Geek in the Park. He asked whether the techniques I used in a jQuery marquee plugin I wrote recently could be used to smooth out CPU spikes that were occurring on his site when the header pattern flowed (see example below)

Scrollable Timeline

For the scrollable timeline I wanted to support both the Plurk version where the user could use their mouse wheel to scroll and the Google version where they could click and drag.

I’ve taken a large chunk of Google’s history to demonstrate the effect.

We set up the page by creating a wrapping div that has overflow: auto; (which we’ll change to overflow: hidden; using jQuery later on). The inner element, the ul in this particular case, is styled to have a width that can accommodate all the nested lis side by side without wrapping on to a ‘new line’ (this is done by floating the lis left and giving them a defined width).

Understanding the Problem

The task is such:

Capture the mouse down event and track the current scroll position and the X co-ordinate of the click.

When the mouse moves, and it’s down, scroll the element by the distance moved from the original mouse down.

When the mouse moves out of the window, trigger a fake mouse up (or cancel the captured down event).

jQuery

We attach 3 built in mouse events: mousedown, mouseup and mousemove. Then we add the jQuery mousewheel plugin before changing the overflow CSS:

// when the DOM is ready...
$(document).ready(function () {
$('#timeline').mousedown(function (event) {
// attach 3 pieces of data to the #timeline element
$(this)
.data('down', true) // a flag indicating the mouse is down
.data('x', event.clientX) // the current mouse down X coord
.data('scrollLeft', this.scrollLeft); // the current scroll position
// return false to avoid selecting text and dragging links within the scroll window
return false;
}).mouseup(function (event) {
// on mouse up, cancel the 'down' flag
$(this).data('down', false);
}).mousemove(function (event) {
// if the mouse is down - start the drag effect
if ($(this).data('down') == true) {
// this.scrollLeft is the scrollbar caused by the overflowing content
// the new position is: original scroll position + original mouse down X - new X
// I'd like to see if anyone can give an example of how to speed up the scroll.
this.scrollLeft = $(this).data('scrollLeft') + $(this).data('x') - event.clientX;
}
}).mousewheel(function (event, delta) {
// now attaching the mouse wheel plugin and scroll by the 'delta' which is the
// movement of the wheel - so we multiple by an arbitrary number.
this.scrollLeft -= (delta * 30);
}).css({
'overflow' : 'hidden', // change to hidden for JS users
'cursor' : '-moz-grab' // add the grab cursor
});
});
// finally, we want to handle the mouse going out of the browser window and
// it not triggering the mouse up event (because the mouse is still down)
// but it messes up the tracking of the mouse down
$(window).mouseout(function (event) {
if ($('#timeline').data('down')) {
try {
// *try* to get the element the mouse left the window by and if
// we really did leave the window, then cancel the down flag
if (event.originalTarget.nodeName == 'BODY' || event.originalTarget.nodeName == 'HTML') {
$('#timeline').data('down', false);
}
} catch (e) {}
}
});

Trovster’s Header Effect

Using the this.scrollLeft DOM attribute again, we can create a completely different effect.

This effect and design was created by Trevor Morris, but the first version he had took up a lot of CPU by changing the CSS left position on the ‘rainbow’ image.

The version I cover in the screencast uses overflows and absolute positioning to keep the CPU usage and the effect (still) smooth in addition to working to make it appear the same if JavaScript is turned off.

Markup

The extra empty div is the wide element and the div#rainbow is the element with the overflow that will scroll.

JavaScript

Note that I’ve called this section JavaScript rather than jQuery, since we’re dealing with the scrollLeft property on the div#rainbow element we only ever need jQuery for the ready event:

$(document).ready(function () {
// capture the rainbow element
var rainbow = document.getElementById('rainbow'),
lastPos, // stores the last scrollLeft position
width = 1656; // the repeating point on the background
// we always reset when the page reloads so that the background is always the same
rainbow.scrollLeft = width;
// use an interval to scroll the rainbow
setInterval(function () {
// subtract to make the background scroll from left to right
rainbow.scrollLeft -= 5;
// if we've hit the beginning then the lastPos will be the same as the scrollLeft
if (lastPos == rainbow.scrollLeft) {
// reset
rainbow.scrollLeft = width;
}
lastPos = rainbow.scrollLeft;
}, 100); // the combination of milliseconds
});

Taking it Further

I would love to see what else you can do: the Coda Slider is another example of the overflow and scrollLeft being used in the same way to create a completely different effect.

What other ways can the overflow/scrollLeft combo be used?

You should follow me on Twitter hereI tweet about jQuery amongst the usual tweet-splurges!