Scroll Up Headline Reader

A couple weeks ago someone on the jQuery discussion list asked if someone could reproduce a rotating headline box in which the headlines, in succession, scroll up into the box, pause, and then scroll up out of the box. Since I already had some code for rotating images on a page, I decided to recycle it and take the challenge.

Here is the finished product. (Please note that if you are looking at this in a feed reader, you won't be able to see the effect. )

Get Started with the HTML and CSS

The first thing I did was throw a simple page together using a single container DIV with an id of "scrollup" and four or five DIVs with a class of "headline" to hold each headline.

[html][/html]

I then added this simple CSS to the scrollup ID and the headline class:

[css]#scrollup { position: relative; overflow: hidden; border: 1px solid #000; height: 200px; width: 200px } .headline { position: absolute; top: 210px; left: 5px; height: 195px; width:190px; }[/css]

That made all of the headlines absolutely positioned in relation to their containing box, which got a height and width of 200px. Notice that the top of all of those headlines is 210px, and the overflow property of the container is set to hidden, so none of the headlines would be showing — until they were ready.

Set Up the jQuery

At the top of the jQuery file I defined variables for the total number of headlines, the interval between headlines, the current headline, and the "old" headline:

[js]var headline_count; var headline_interval; var current_headline = 0; var old_headline = 0;[/js]

Then I set the headline_count variable to be the number of div elements that have a class of "headline." But that number can't be computed until the DOM is loaded, so I wrapped the variable in jQuery's $(document).ready() function. I also wanted to set the first headline's "top" property so that it would be immediately visible, while the others would remain hidden — at least initially — below the box (as set in the CSS):

[js]$(document).ready(function(){ headline_count = $("div.headline").size(); $("div.headline:eq(" + current_headline + ")").css('top', '5px'); }); [/js]
The code examples for this entry used to include the .top() method, which, as of jQuery 1.1, no longer works. You will need to use .css('top', 'Npx') instead.

Before we move on, I'd like to point out a few things about the above code:

  1. jQuery has a .size() function that is similar to JavaScript's length property in that it returns the number of jQuery objects defined by the $() that comes before it.
  2. The second line is only fired once, when the DOM is first loaded. Instead of setting every headline's top to 5px, it uses a special jQuery DOM selector, :eq(), to set only the current headline's top. Typically, :eq() would take a number, like so: $("div.headline:eq(0)").
  3. I chose to pass in the "current_headline" variable instead of a number to allow for a little flexibility. If I later decide I want to start with the fourth headline, for example, I'd just have to change var current_headline = 0 up at the top of the script to var current_headline = 3. In order to get the variable in there, though, I had to concatenate it with the rest of the selector before and after it.

Everybody Rotate!

Now that everything was in place, I could write my headline_rotate() function. I first needed to increment each headline by one until I reached the last one and then start over, creating a loop. To do so, I used what my friend Jonathan Chaffer told me is called "clock arithmetic." Here is what that looks like (at least, the way I did it):

[js]function headline_rotate() { current_headline = (old_headline + 1) % headline_count; }[/js]

Line 2 sets a new value for current_headline by first adding 1 to old_headline and then using the modulus operator (%) to get the remainder of old_headline + 1 (our new headline) divided by headline_count (total number of headlines). Jonathan explained it this way: "the remainder will always equal old_headline + 1 until it reaches headline_count, at which point the remainder becomes zero." The only thing better than having a genius working next to me is having a genius who is great at explaining things to mere mortals like me. :)

Add the Animation

Next I added the animation into the headline_rotate() function — moving the old headline up and out of sight while moving the next headline (now called current_headline) into view.

The old headline movement actually has two parts: (a) scrolling up and out of sight and (b) moving instantly back down underneath the box so that it's ready to slide up into the box again the next time. This is where jQuery's "callbacks" come in very handy. I could queue the second effect by putting it in the callback of the first. Compare just the first effect...

[js]$("div.headline:eq(" + old_headline + ")").animate({top: -205},"slow")[/js]

...to the first, plus the second in the callback...

[js]$("div.headline:eq(" + old_headline + ")").animate({top: -205},"slow", function() { $(this).css('top', '210px'); });[/js]

By the way, the -205 in .animate({top: -205}) means that the top of the headline moves to 205 pixels above the top of its containing element (because the containing element had its position set to relative) so that we're sure the entire headline clears the box.

For the current headline, I simply moved its top up to 5 pixels below the top of the scrollup box so that it would be visible. And after that, I made old_headline equal current_headline:

[js]$("div.headline:eq(" + current_headline + ")").animate({top: 5},"slow"); old_headline = current_headline; }[/js]

To get the function to run when the page loaded, I simply dropped headline_rotate() inside my $(document).ready(). Unfortunately, that only made the animation fire once. I wanted it to repeat.

And Repeat

Remember that headline_interval variable I included way at the top? Here is how I used it:

Still inside $(document).ready(), I replaced headline_rotate() with headline_interval = setInterval(headline_rotate,5000);. That made JavaScript's setInterval() trigger my headline_rotate() function once every 5000 milliseconds (5 seconds).

Bonus – Pause on Hover!

I was fairly satisfied with the way the headline rotator worked, but I realized that for it to be really useful I'd want to make it pause when the mouse would hover over the box and start up again as soon as the mouse went back out. I'm not sure that I did it the best way, but it seems to have worked fairly well for me. I attached jQuery's hover() method to the "scrollup" div. The hover() takes two arguments, the first for mouseover and the second for mouseout. In the mouseover part, I stop the "interval," and in the mouseout part, I started it back up and called headline_rotate() again so the next headline would show up immediately:

[js]$('#scrollup').hover(function() { clearInterval(headline_interval); }, function() { headline_interval = setInterval(headline_rotate,5000); headline_rotate(); });[/js]

That hover stuff also went inside $(document).ready(), and then I was done!

The Full Code

I set up a demo page with the full code and CSS embedded in the <head>, so if you want to take a look and copy it for your own use, go ahead. Or, take a look at the full jQuery code below:

[js]var headline_count; var headline_interval; var old_headline = 0; var current_headline = 0; $(document).ready(function(){ headline_count = $("div.headline").size(); $("div.headline:eq("+current_headline+")").css('top', '5px'); headline_interval = setInterval(headline_rotate,5000); $('#scrollup').hover(function() { clearInterval(headline_interval); }, function() { headline_interval = setInterval(headline_rotate,5000); headline_rotate(); }); }); function headline_rotate() { current_headline = (old_headline + 1) % headline_count; $("div.headline:eq(" + old_headline + ")") .animate({top: -205},"slow", function() { $(this).css('top', '210px'); }); $("div.headline:eq(" + current_headline + ")") .animate({top: 5},"slow"); old_headline = current_headline; }[/js]

If you have any questions about the code or suggestions for improvement, please leave a comment.

UPDATE

Jonathan Chaffer and I wrote a much-improved version of this headline reader and discuss it in detail in the Learning jQuery book.You can see a demo of it on the companion site. Also, Mike Alsup has written the brilliant Cycle plugin that does the scroll-up effect and much, much more. Check it out!