Semantic/Degradable Ticker

Superbowl XXXVIII

Total Net Yards

Total Plays

Average Gain

The idea for this came from the information ticker that CBS was running during Superbowl XXXVIII -- it would pop up in the lower left corner of the screen with data on each team's passing yards, rushing, etc for the quarter.

The CBS logo sat in the top right corner of the information ticker and would slide over the heading text, and then slide back to it's corner revealing a new heading and new game data. I thought it was kind of cool and made a mental note to write something similiar for the web.

When I first began working on this last evening, my intention was to do the entire thing with javascript, using appendChild and createElement just like I do for games. That proved to not be much of a challenge, so I thought that a ticker coded with semantically meaningful markup that would degrade gracefully for non-standards compliant browsers as well as browsers without javascript would be cool. So, the focus became less on providing a ticker, and more a way of doing it to make it useful in any scenario, including people with huge font sizes.

Try the following:

The one thing on this that really bugged me was that it is apparently invalid to use a NOSCRIPT tag to do anything useful, such as define a fallback style sheet. Originally, the H2's and UL's were set to display:none in the style sheet and then a seperate style sheet was placed between NOSCRIPT tags that would set them to display:block. This allowed the ticker to show only the first content block when the page was loaded, and would cause users without javascript enabled to see them all.

Since that is invalid markup, I had to remove the display:none; from the style sheet, remove the fallback style sheet and apply the display:none; to the elements in a for loop in the init method. This has the unfortunate consequence of flashing all the content when the document loads. The valid uses of NOSCRIPT really need to be re-evaluated by the W3C, in my opinion.

And finally, one known issue - if you bump up the font size several points and then reduce it to the original size the mask element will remain well outside of the H2 element. I dont know how many people continually change their font size from large to small and back again so I'm not particulary concerned. It should be noted that if you arrive on the page with a larger than defined font size, the mask element will place itself in the appropriate place.

There you have it. Fairly solid, I think. If you found something that will break it, please let me know.

HTML

<div id="mContainer">
	<h1>Superbowl XXXVIII</h1>
	<h2 id="data0"><span>Total Net Yards</span></h2>
		<ul id="data0_ul">
			<li>New England: <strong>481</strong></li>
			<li>Carolina: 387</li>

		</ul>
	<h2 id="data1"><span>Total Plays</span></h2>
		<ul id="data1_ul">
			<li>New England: <strong>83</strong></li>
			<li>Carolina: 53</li>
		</ul>
	<h2 id="data2"><span>Average Gain</span></h2>
		<ul id="data2_ul">
			<li>New England: 5.8</li>
			<li>Carolina: <strong>7.3</strong></li>
		</ul>
</div>

Style Sheet:

body {
	font:9pt arial;
	margin:5px;
}

#mContainer {
	position:relative;
	width:200px;
	margin:auto;
	overflow:show;
}

#mContainer h1 {
	margin:0;
	font:9pt arial;
	font-weight:bold;
	background-color:#98B4C1;
	width:12em;
	border:1px solid #000;
	border-bottom-style:none;
	color:#000;
	text-align:center;
	padding:2px 0 2px 0;
	overflow:hidden;
	height:1em;
}

#mContainer h2 {
	margin:0;
	font:9pt arial;
	font-weight:bold;
	width:15em;
	padding:0;
	height:1.2em;
	background-color:#638DA1;
	color:#FFF;
	border:1px solid #000;
}

#mContainer h2 span {
	padding-left:5px;
}

#mContainer ul {
	margin:0;
	padding:0;
	font:9pt arial;
	background-color:#E2EAED;
	border:1px solid #000;
	border-top-style:none;
	width:15em;
	height:3em;
}

#mContainer ul li {
	list-style-type:none;
	height:15px;
	font:9px verdana;
	padding-left:10px;
	margin:0;
}

#mask {
	position:absolute;
	padding:0;
	font-size:9pt;
	width:15px;
	height:1.2em;
	background-color:#638DA1;
	background-image:url(mask_bg.gif);
	background-repeat:repeat-y;
	left:188px;

}

Javascript Source:

this.onload = init;
var tickerIndex = 0; // indicates the first bit of info to show
var maxTicker = 3; // indicates the total number of UL's +1

var startInterval = 3000; // number of milliseconds to wait before clearing the first UL
var maskDirection = 0; // direction of the mask element. 0: left; 1: right;
var maskIncrement = 5; // number of pixels to move the mask element at a time

function init() {
	// hide H2's and UL's that arent the first 
	for(i=0;i<maxTicker;i++) {
		document.getElementById("data" + i).style.display = "none";
		document.getElementById("data" + i + "_ul").style.display = "none";
	}
	// create the div element used for the mask;
	msk = document.getElementById("mContainer").appendChild(document.createElement("div"));
	msk.id = "mask";

	// display the first UL and H2
	document.getElementById("data" + tickerIndex).style.display = "block";
	document.getElementById("data" + tickerIndex + "_ul").style.display = "block";
	t = document.getElementById("data" + tickerIndex).offsetTop;
	msk.style.top = (t+1) + "px";
	l = document.getElementById("data" + tickerIndex).offsetWidth;
	l-=16;
	msk.style.left = l + "px";
	// call the beginTicker function in 2 seconds.
	zInterval = setTimeout("beginTicker()",2000);

}

function beginTicker() {
	clearInterval(zInterval);
	maskDirection = 0;
	// begin the mask animation interval
	zInterval = setInterval("animateSlide()",20);
}

function animateSlide() {
	// get the current width and left of the mask
	curWidth = document.getElementById("mask").offsetWidth;
	curLeft = document.getElementById("mask").offsetLeft;

	// increment/decrement those values based on maskDirection
	// define a border in #mask to see whats happening here if the code isnt clear
	if(!maskDirection) {
		curWidth+=maskIncrement; curLeft-=maskIncrement;
	} else {
		curWidth-=maskIncrement; curLeft+=maskIncrement;
	}

	document.getElementById("mask").style.width = curWidth+ "px";
	document.getElementById("mask").style.left = curLeft + "px";
	t = document.getElementById("data" + tickerIndex).offsetTop;
	document.getElementById("mask").style.top = (t+1) + "px";
	maxLeft  = document.getElementById("data" + tickerIndex).offsetWidth - 16;
	if(curLeft<=0)  {
		// the mask is at the full left of the ticker. hide the current H2 and UL
		document.getElementById("data" + tickerIndex).style.display = "none";
		document.getElementById("data" + tickerIndex + "_ul").style.display = "none";
		// increment tickerIndex. if its higher than maxTicker, reset it to zero.
		tickerIndex++;
		if(tickerIndex>=maxTicker)tickerIndex = 0;
		// show the next set of H2 and UL's
		document.getElementById("data" + tickerIndex).style.display = "block";
		document.getElementById("data" + tickerIndex + "_ul").style.display = "block";

		// set maskDirection to 1 so it will animate to the right
		maskDirection = 1;
	} else if (curWidth<=15) {
		// the mask element is all the way to the right of the ticker.
		// clear the animation interval and start over again.
		clearInterval(zInterval);
		zInterval = setTimeout("beginTicker()",2000);
	}
}

function degrade() {
	clearInterval(zInterval);
	for(i=0;i<maxTicker;i++) {
		document.getElementById("data" + i).style.display = "block";
		document.getElementById("data" + i + "_ul").style.display = "block";
	}
	document.getElementsByTagName("link")[0].href = "";
}

Semantic Ticker v1.0
last revision: 02.03.2004
steve@slayeroffice.com
http://www.slayeroffice.com