Search

Fun with Overflows

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.

Background

The first is a scrollable timeline. A couple of readers requested a demo of how Plurk’s browse timeline works. In addition, in the last month, Google released a 10 year timeline – so I wanted to show how this works.

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)

Watch

Part 1: Scrollable Timeline

Watch the scrollable timeline screencast (alternative flash version)

QuickTime version is approximately 60Mb, flash version is streaming.

View the demo and source code used in the scrollable timeline screencast

Part 2: Trovster’s Header Effect

Watch the Trovster’s header effect screencast (alternative flash version)

QuickTime version is approximately 45Mb, flash version is streaming.

View the demo and source code used in the Trovster’s header effect screencast

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:

  1. Capture the mouse down event and track the current scroll position and the X co-ordinate of the click.
  2. When the mouse moves, and it’s down, scroll the element by the distance moved from the original mouse down.
  3. When the mouse moves out of the window, trigger a fake mouse up (or cancel the captured down event).

Example of drag method

CSS

I won’t cover all the CSS used, only the key style:

#timeline {
  height: 375px; /* fixed */
  overflow: auto; /* changed to hidden via JavaScript */
}

.tl-events { /* the UL */
  width: 11800px; /* the width required to hold all the info */
}

.tl-events li {
  float: left; /* allows the lis to stack against eachother */
  width: 300px;
}

jQuery

This is the full code listing used in the demo.

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.

Example of drag method

Markup

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

<div id="headerEffect">
  <div id="rainbow"><div></div></div>
  <div id="swirl"></div>
</div>

CSS

In addition to the following CSS, I also hand coded the PNG transparency for IE6 to work when JavaScript is disabled.

#headerEffect {
  position: absolute;
  width: 100%;
  height: 400px;
  overflow: hidden;
  top: 0; /* make sure IE stretches it properly */
  left: 0;
}

#rainbow {
  height: 400px;
  width: 100%;
  overflow: hidden;
}

#rainbow div {
  height: 400px;
  width: 3312px; /* nice big width that ensures it repeats */
  background: url(/css/img/header.colour.3.png) repeat-x scroll -20% 0;
}

#swirl {
  background: url(/css/img/header.swirl.png) no-repeat scroll 50% 0;
  height: 400px;
  width: 100%;
  position: absolute; /* places the swirl *over* the rainbow */
  top: 0;
  left: 0;
}

In addition I’ve included IE6 specific styles:

<!--[if lte IE 6]>
<style type="text/css" media="screen">
/* I've put the alpha transparency in the CSS so as to support non JS enabled visits */
#swirl {
  background-image: none;
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://www.trovster.com/css/img/header.swirl.png', sizingMethod='scale'); 
}
</style>
<![endif]-->

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?

Related screencasts

Demo

If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.

Source Code

Comments

  1. Zach Langley On 16th October 2008 at 20:10

    Remy,

    Thanks for this great post.

    In order to make it scroll faster I believe you want to multiply the delta by a certain value, not add or subtract some value to or from that delta.

    this.scrollLeft = $(this).data(‘scrollLeft’) + ($(this).data(‘x’) – event.clientX) * 2; // twice as fast

    Regards, Zach

  2. Remy On 16th October 2008 at 21:10

    @Zach – of course! I was even using it on the mouse wheel plugin. Thanks!

  3. Priest On 16th October 2008 at 21:10

    Remy, love the site, the screencasts, and the tips. Keep them coming.

    Just wanted to make a comment on CPU usage. Now, I’m using a Dell dual-core 3.0GHz machine with 2.0GB of RAM. Windows XP.

    I looked at that page through Firefox 2.x and I heard my CPU fan kick up. I looked at the usage and it was idling at about 55-60% usage (overall).

    I loaded the same page up in IE6.x and the CPU usage only varied between 15-20% usage.

    I have no idea why it was chomping down so much more usage in Firefox than in IE, but I figured I’d mention.

  4. Remy On 16th October 2008 at 21:10

    @Priest – interesting stuff. I wonder if Firefox 3 is any better…?

    The CPU throttling is very much down to the timeout and the amount the div moves. Those two together affect how often the browser has to render – on top of which it’s rendering alpha transparency, which (I would imagine) is generally harder than images with no alpha.

    I remember reading that Safari 2 would still animate gif even when they were out of view (or on another tab) and would still consume CPU, so it’s very much down to how each browser has implemented their render engine.

    Let us know if you have a chance to play around with the numbers to see how it affects the CPU usage.

    Thanks!

  5. AJK On 16th October 2008 at 22:10

    Here’s a timeline I did for the bbc a year or so ago.

    http://www.bbc.co.uk/music/sevenages/

  6. Remy On 16th October 2008 at 22:10

    @AJK – nice. Very rock and roll ;-)

  7. k3k On 22nd October 2008 at 07:10

    I little complicated for the end-user usability, but nice. thanks

  8. scott On 25th October 2008 at 20:10

    Hi Remy,

    Thank you so much for this post. It is much appreciated.

    When using your timeline demo page using Safari on a Mac, my mouse-up gets lost if it happens outside of the timeline region. That is, I start inside the timeline region, click and drag the mouse outside the region, and then release. When I go back into the region with the mouse button up, your code still thinks it is down and it scrolls as i move the mouse.

    Is there an easy fix for that behavior?

    Thanks,

    Scott

  9. tammy G On 26th October 2008 at 11:10

    is it possible to scroll the timeline on mouse over? If so, could you show how this can be done, please

  10. sam flowers On 4th November 2008 at 06:11

    tammy,

    I do not think thats possible! At least i can not do it.

    Thansk

  11. Ben On 19th November 2008 at 10:11

    You should use cursor: ‘col-resize’ it explains the function better and it work on more browsers. With moz- the function isn’t explained by his self on the most browsers.

  12. Water On 23rd November 2008 at 23:11

    I little complicated for the end-user usability, but nice. thanks

  13. TJ On 3rd December 2008 at 11:12

    Great post! Thanks! I’m trying to create an apple phone effect with the Scrollable Timeline so when the user scrolls there us a faster movement but also when they let go the timetable moves for a few more seconds with a nice slow down ease to it. Do you know if this can be done and if so how? Thanks.

  14. Chris Mahon On 20th February 2009 at 14:02

    Cheers for the tutorial Remy, awesome stuff as usual. Implemented it on my website here – http://thumbslap.com although I was having some problems with the drag functionality :(

  15. العاب On 29th March 2009 at 03:03

    is it possible to scroll the timeline on mouse over? If so, could you show how this can be done, please

    and for me

  16. Lungos On 22nd April 2009 at 09:04

    AJK your timekine for the BBC is great !

  17. Antti On 1st May 2009 at 13:05

    I think you could also add a ‘return false’ at the end of the mousewheel event attachment – This would prevent the window from scrolling horizontally AND vertically if the page has content that exceeds the height of the browser window.

  18. Dave Bowker On 18th June 2009 at 11:06

    Have added in cursor changes to work across multiple browsers when supported, speed option by Zach, and return false; by Antti.

    http://snipplr.com/view/16062/jquery-overflow-timeline-effect/

  19. Shibi Kannan On 18th June 2009 at 15:06

    Very nice code, amazing effects, I am wondering whether this can be adapted to WordPress posts to scroll by timeline. Any ideas on how to implement for WordPress.

  20. ConradH On 10th August 2009 at 07:08

    Thanks for this code. It works and looks great. One issue though – the overflow: hidden does not seem to take effect for me for some reason. The #timeline div displays with a scrollbar and when I check using firebug the overflow never changes from auto to hidden. Any help would be greatly appreciated.

    Shibi – Just use the loop or write your query and make sure to wrap the result you want to scroll in div id=timeline and ul class=tl-events and each individual result in an li tag and everything should work fine.

  21. nerdstalker On 29th August 2009 at 17:08

    Would be great if it had a visible horizontal scroll as a usability enhancement.

  22. James Ison-Stierer On 29th September 2009 at 10:09

    I’m very impressed! Good work – only found your website today but already spent half my day on it!

  23. Nick On 7th November 2009 at 15:11

    Hey there, great tutorial. I’m having a little trouble with IE (of course), where the mouse doesn’t always release. I can see a guy called Scott (from 25th Oct. ’08) had the same problem with Safari on a Mac. Mine is IE8 (WindowsXP). Many thanks in advance for any help given (from anyone).

  24. Peter On 26th November 2009 at 07:11

    This is a great script! I’m going to use this for a Red Cross Tsunami commemoration timeline. I have also added the Colorbox jquery plugin for displaying pictures and media.

    My issue is that after adding colorbox while the timeline still has the click and drag functionality it has lost the functionality to scroll with the mouse wheel. As a designer primarily, I don’t know how to fix the issue, but I have narrowed down the focus to some sort of conflict when the jquery.min.js file is linked to for the colorbox plugin. The mouse wheel scrolling returns if I remove the link to this file in the head.

    Any suggestions would be welcomed.

    I have the inital part of the project up and running here: http://www.digitalcoffee.com.au/clients/rc/timeline/

  25. Peter On 26th November 2009 at 16:11

    I got a solution – I added in the color box includes and file links before the jquery slider includes. I think previously some of the values set in jquery.js (for the scroller) were getting reset/chnaged when I included jquery.min.js (for colorbox) later in the document. I just switched the order of the includes around and it works fine now! Wow. Thanks for the script – it will make a nice presentation for Red Cross Australia.

Comments are now closed.