In
this tutorial we'll be looking at how we can transform some semantic
and accessible underlying HTML into an attractive and functional news
ticker that smoothly scrolls its contents. Some news tickers are
horizontal and some are vertical; the one that we're going to create
today will be vertical.
The context of the example is a news scroller so we'll be working with
plain text, but we should be able to put whatever we wanted into the
underlying mark-up; images, or links, or whatever. We'll be using
jQuery as the underlying JS library, and a little HTML and CSS. Let's
make a start.
The Underlying HTML
In a new page in your text editor add the following code:
This is a news title! This is a snippet of the news. It could be the whole news item or it could link to another page containing...
News Heading 2 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
News Heading 3 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
News Heading 4 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
This item is long! Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Save this as simpleTicker.html in a directory containing jQuery 1.3.2.
As well as the usual page furniture - the DOCTYPE, META content-type,
title, etc - we have a custom style sheet that we'll create in just a
moment and we link to jQuery at the end of the body (for performance
reasons).
On the page is the content that we'll progressively enhance into the
news ticker; it's made up of a simple definition-list element, which
feels appropriate for our purposes. Although only inline content can be
placed in each <dt> element, block-level content can be put in
the <dd> elements.
The code is minimal and highly accessible; browsers, mobile
devices and screen-readers should all have no difficulty interpreting
and rendering it. With no styling however, it does look pretty
shocking:
Providing Default Styling
Let's add some basic styling; even with JavaScript switched off, no
one wants to see the list as it is at the moment. In a new file in your
text editor add the following code:
#ticker {
width:180px; height:300px; overflow:auto; border:1px solid #aaaaaa;
}
#ticker dt {
font:normal 14px Georgia; padding:0 10px 5px 10px;
background-color:#e5e5e5; padding-top:10px; border:1px solid #ffffff;
border-bottom:none; border-right:none;
}
#ticker dd {
margin-left:0; font:normal 11px Verdana; padding:0 10px 10px 10px;
border-bottom:1px solid #aaaaaa; background-color:#e5e5e5;
border-left:1px solid #ffffff;
}
#ticker dd.last { border-bottom:1px solid #ffffff;
Save this file in the same directory as the page and call it
simpleTicker.css. We give the list a set width and height and set the
overflow property to auto; the height of the ticker is less than the
space required to show all of the news items so the scrollbar will
allow visitors with JavaScript disabled to view all of the content.
Some of the styles are purely presentational; anything that sets a
background-color, border or font is totally arbitrary and is used to
make the example a little more attractive. The widget should now look
like this:
However minimal we choose to make it, it's a big improvement on
the default rendering; it would quite happily fit into a sidebar or
column now; it's an acceptable fallback from the finished widget and a
great foundation from which to progressively enhance.
Progressively Enhancing the Ticker
Now we can move on to the fun part - adding the JavaScript that will
turn this from a simple list into an automatic ticker; in the empty
<script> element at the bottom of the page add the following
code:
$(function() {
//cache the ticker
var ticker = $("#ticker");
//wrap dt:dd pairs in divs
ticker.children().filter("dt").each(function() {
var dt = $(this),
container = $("");
dt.next().appendTo(container);
dt.prependTo(container);
container.appendTo(ticker);
});
//hide the scrollbar
ticker.css("overflow", "hidden");
//animator function
function animator(currentItem) {
//work out new anim duration
var distance = currentItem.height(),
duration = (distance - Math.abs(parseInt(currentItem.css("marginTop")))) / 0.025;
//animate the first child of the ticker
currentItem.animate({ marginTop: -distance }, duration, "linear", function() {
//move current item to the bottom currentItem.appendTo(currentItem.parent()).css("marginTop", 0);
//recurse
animator(currentItem.parent().children(":first"));
});
};
//start the ticker
animator(ticker.children(":first"));
});
All of our code is encased within the jQuery document.ready short-cut
$(function(){ }) which will execute the anonymous function as soon as
the page has loaded. The first thing we do is to cache our main
selector, which is the outer <dl> element; this will save us from
having to repeatedly select the element from the DOM whenever we want
to work with it and improves the performance of the page.
The underlying definition list contains both definition term
and definition description elements, all of which could potentially be
of varying sizes. To make things a little easier in our script we next
wrap each <dt> and <dd> pair in a containing <div>
element; this allows us to group the different elements logically and
smooth out the animations. It also means that we can use whatever
margins and padding on the *lt;dt> and
elements without needing to take these into consideration in our animation calculations.
To do this we select all of the children from our cached selector and
use the jQuery filter method to discard all of the <dd> elements.
Then for each remaining <dt> we create a new container
<div> and append both the <dt> and the <dd> to the
new container. We have to add the <dd> that follows the current
<dt> first, because once the <dt> has been appended to the
container it won't have a <dd> after it. We therefore need to use
the prepend method on the <dt> once the <dd> has been
appended.
Once all of the <dt> and <dd> pairs have been
wrapped in <div> elements we then change the ticker's overflow
CSS property to hidden so that the scrollbar is removed. This is part
of the accessibility strategy and ensures that only visitors with
JavaScript enabled get to see the enhanced ticker.
Next we define our animator function, which is where we'll set
the animation which causes the ticker to begin scrolling and then keep
scrolling indefinitely. This is a regular JavaScript function which
accepts a single parameter; when we call this function we'll pass in
the first container element within the ticker.
The first thing we do within this function is work out the distance
that the selected element has to travel and the duration of the
animation. The distance part is easy; it's just the height of the
current container element.
The duration is a little more complex; it's the total distance to
travel minus the distance already traveled, divided by a baseline speed
that I've arbitrarily set at 0.025. The height will already be an
integer but the css method will return a string representing the
margin-top of the element being animated so we need to use JavaScript's
parseInt() function to convert it to a number. It will also be a
negative number so we use the Math.abs() JavaScript function to convert
it from a negative to an absolute (positive) number.
The baseline speed that we divide the remaining distance to
travel by is pretty slow in my opinion; I've seen tickers before (built
with Java) and they've been approximately the same speed and I didn't
get motion sickness or suffer any ill effects. If you feel that the
animation is too fast (or even too slow) feel free to change this
figure. It needs to be a really small number though; anything in the
range of 0.01 to 0.09 is probably going to be ok.
Once we have these figures we can then create the animation
using jQuery's animate method. The first parameter for this method is
the actual animation itself; we want the first of the container
elements in the ticker to be moved upwards until it is completely
hidden in the overflow, but we also want to drag the rest of the
container elements up by the same distance as well so we use adjust the
marginTop of the element.
The second argument of the animate method is the duration which
we worked out a moment ago. The third argument is the easing method;
custom jQuery animations automatically get two types of easing - swing
and linear. Swing causes the animation to speed up and slow down, while
linear makes it sooth and continuous, so this is the option for us in
this situation.
The final argument is an anonymous callback function that will
be executed when the animation ends. Within this function we move the
element that has just been animated to the end of the set of
containers, reset its marginTop back to 0, and then recursively call
the animator function again, passing in the new first-child of the
ticker.
Finally, we need to call the animator function once to start
off the whole process. When we view this page in a browser, the ticker
should smoothly scroll through each of the elements in the list
repeatedly:
Starting and Stopping
Now we need to think about what we want to happen when a visitor
mouses-over a news item; personally I think the default behavior should
be that it pauses on mouse-over and then restarts again on mouse out.
We can easily add this behavior with a couple of jQuery event handlers;
add the following code just before the final brace/curly brace combo:
//set mouseenter
ticker.mouseenter(function() {
//stop current animation
ticker.children().stop();
});
//set mouseleave
ticker.mouseleave(function() {
//resume animation
animator(ticker.children(":first"));
});
Both of these functions are really simple; we use mouseenter and
mouseleave instead of mouseover and mouseout because it makes these
functions nice and small; we don't need to worry about ignoring
mouseouts when the pointer moves from the outer ticker element to one
of the child elements. In the mouseenter function we simply stop the
animation. In the mouseleave function we just restart the animation
once more, passing in the first-child of the ticker.
Fixing IE
We have one problem left to solve, and fortunately it's an easy one.
Running the page as it is in IE (tested in version 8) throws an error
as soon as it is loaded. The error is caused because the very first
time the animator function runs the first container <div> will
have its margin-top set to auto by the browser. Other browsers will
interpret the margin-top as 0. Which browser is correct is debatable,
although I'll let you draw your own conclusion about the fact that IE
is the only browser that causes the problem. All we need to do to fix
it is explicitly set the margin-top of the container elements. We can
do this by adding the following line of code to the style sheet:
#ticker div { margin-top:0; }
Now the page should work in IE as well:
Summary
This now brings us to the end of the tutorial, we should now have a
simple but effective news ticker that can be put to use in a variety of
ways. When using text, like in this example, defining the headings and
associated content sections within a definition list makes sense from a
semantics perspective, and it's easy to add any additional elements we
may need as we go.