Pages

Tuesday, March 9, 2010

How To: Create A Scrolling News / Information Marquee using JavaScript and Server Side Code

Have you ever wanted to add a continuous news scroller at the bottom of your web page, that was dynamically populated from database data at runtime? This will show you all of the pieces needed to create the scroller and update it server side in code behind. The main parts are the news scroller itself, built using JavaScript, and the content that populates the scroller which is done server side in VB.NET. I will say while this is not overly complex, it does have several parts and pieces that go together, so beginners may get a bit confused. If this is the case, just take your time and try this on a new blank page before attempting to inject this into existing logic. Also remember that all JavaScript is case sensitive, so be careful when making calls to the functions.

Let's get right into the code. I will give credit to dhtmlgoodies where I was able to get some of the initial concepts for scrolling. I have since greatly expanded on the basics by populating the contents server side, impoving the scrolling visually, and an AJAX asynchronous postback behind the scenes each time the scroller has moved across the page to get updated data. I will leave out the AJAX portion of this because it is an entire post in itself, and we will just get the server side populated scroller working 1st. To being we are going to create a placeholder 'DIV' tag on the page. The 'innerHTML' code will be created and set server side. This is just the container:














Notice above, the call to initialize the marquee. This is a call to a JavaScript method which will be explained a little later on. For now we need to make sure that call is made, so that the scrolling effect will begin when the page has completed loading. Within this call I sent it a page defined variable value, which is exposed server side. You might ask, why do it this way and not just declare inline document.getElementById...etc. Well I often use ASP.NET Master and Content pages, and the control naming structure for content pages is a bit more complex than calling a control directly. In this situation exposing the value server side is a much easier alternative to hardcoding its value. If you are using a traditional page, you can probably streamline this method and extract the control's ID directly.

Next, here is the .css class that was defined in the 'DIV' element above and the class defining the style of the text that will be inside of that DIV. You can modify this as needed, however the classes must exist as named or the JavaScript will not work properly.


.ScrollingMarqueeStyle
{
padding-top:3px;
height:25px;
background-color: #000;
position:absolute;
left:0px;
z-index:1000;
bottom:0px;
display:none;
width:100%;
overflow:hidden;

/*Border that hovers over the scrolling marquee*/
border-top: solid 2px red;
}
body > div.ScrollingMarqueeStyle
{
/* Firefox */
position:fixed;
}
.ScrollingMarqueeStyle .MarqueeTextObj
{
/* Text within marquee */
position:absolute;
color: #FFF;
font-weight:bold;
white-space:nowrap;
font-family: Trebuchet MS;
font-size: 16px; /*This is the size of the scrolling text*/
}
/* Applies to custom classes used in <Span&rt; tags within the scrolling text */
.DescriptionText
{
color:#66FF33;
}
Now let's take a look at the JavaScript that works the magic behind the scrolling marquee. There are (5) functions: InitHorizontalScrollMarquee, MoveScrollingMarquee, PositionScrollingMarquee, Stop_MoveScrollingMarquee, and Resume_MoveScrollingMarquee. These can all be placed either within a tag in the head of the page, or within a .js file that is referenced by the page.

// Higher = Faster, Lower = slower and more smoothly (equates to pixels per move on the screen)
var marqueeSteps = 1;
// Lower value = Faster (# equates to Milleseconds - ms between moves. Watch out for CPU usage if too low)
var marqueeSpeed = 25;
// Make the marquee stop moving when user moves his mouse over it, set to 'true' to enable, 'false' to disable.
var marqueeStopOnMouseOver = true;
// "top" or "bottom" position set here
var marqueePosition = 'bottom';

/* Don't change anything below here */
var marqueeObj;
var marqueeTextObj;
var marqueeTmpStep;
var marqueeTextObjects = new Array();
var marqueeHiddenSpans = new Array();
var marqueeIndex = 0;

var hScrollRefreshIntervalId;

function PositionScrollingMarquee(e, timeout) {
if (document.all) e = event;
if (marqueePosition.toLowerCase() == 'top') {
//Place the marquee at the top of the page
marqueeObj.style.top = '0px';
}
else {
//Place the arquee at the bottom of the page
marqueeObj.style.bottom = '-1px';
}
if (document.all && !timeout) setTimeout('PositionScrollingMarquee(false,true)', 500)
}


function MoveScrollingMarquee() {

//Get left-side position of the marquee
var leftPos = marqueeTextObj.offsetLeft;

//Subtract step count set at global level and kept track of in InitMarquee (Move DIV left)
leftPos = leftPos - marqueeTmpStep;

//Left position of DIV + [array value] + width of a single 'marqueeTextObj'
var rightEdge = leftPos + marqueeHiddenSpans[marqueeIndex].offsetLeft + marqueeTextObj.offsetWidth;

//If the right edge calculated above is less than 0, this means the ENTIRE marquee is off
//the left side of the page. At this point reset the scrolling marquee to the right side of the page to re-scroll.
if (rightEdge < 0) {


//Reset the marquee to be just off the right side of the page which is done
//by making its left position the width of the page
leftPos = document.documentElement.offsetWidth;
marqueeTextObj.style.left = leftPos + 'px';

//Set marquee properties
marqueeTextObj.style.display = 'none';

marqueeIndex++;
if (marqueeIndex >= marqueeTextObjects.length) marqueeIndex = 0;
marqueeObj = marqueeTextObjects[marqueeIndex];

marqueeTextObj.style.display = 'block';

}

//Move the DIV to the left position calculated above. This is where the 'magic' happens
//and the 'scrolling' occurs.
marqueeTextObj.style.left = leftPos + 'px';

}

function Stop_MoveScrollingMarquee() {
if (marqueeStopOnMouseOver) marqueeTmpStep = 0;
}


function Resume_MoveScrollingMarquee() {
marqueeTmpStep = marqueeSteps;
}

function InitHorizontalScrollMarquee(MarqueeDivControlID) {

//Get the marquee object properties using the ID passed in from the calling code:
marqueeObj = document.getElementById(MarqueeDivControlID);


var spans = marqueeObj.getElementsByTagName('DIV');
for (var no = 0; no < spans.length; no++) {

//Find the 'MarqueeTextObj' class and set its properties
if (spans[no].className == 'MarqueeTextObj') {
marqueeTextObj = spans[no];
spans[no].style.display = 'block';

marqueeTextObjects.push(spans[no]);
var hiddenSpan = document.createElement('SPAN');
hiddenSpan.innerHTML = ' '
spans[no].appendChild(hiddenSpan);
marqueeHiddenSpans.push(hiddenSpan);
}
}
if (marqueePosition.toLowerCase() == 'top') {
marqueeObj.style.top = '0px';
}
else {
if (document.all) {
marqueeObj.style.bottom = '0px';
}
else {
marqueeObj.style.bottom = '-1px';
}
}

//Set the left position of the marquee = to the width of the page which essentially places
//its leftmost part at the right end of the page.
marqueeTextObj.style.left = document.documentElement.offsetWidth + 'px';

//Define the mouse events; these are wired up to the varibale named 'marqueeStopOnMouseOver' which is false by default.
marqueeObj.onmouseover = Stop_MoveScrollingMarquee;
marqueeObj.onmouseout = Resume_MoveScrollingMarquee;
if (document.all) window.onscroll = PositionScrollingMarquee; else marqueeObj.style.position = 'fixed';

marqueeObj.style.display = 'block';
marqueeTmpStep = marqueeSteps;

//setInterval() returns an interval ID, which later can be passed to clearInterval(): to stop repeat calls if desired;
hScrollRefreshIntervalId = setInterval('MoveScrollingMarquee()', marqueeSpeed);

}

I will touch briefly on the JS code, but I tried to comment up the code well so you can read the documentation within. Basically, 'InitHorizontalScrollMarquee' is called, and finds the DIV we created server side with the class named 'MarqueeTextObj' to set its properties. Look closely at the JS variables defined prior to all of the functions. These dictate a lot of the behavior attributes like position (top or bottom), speed, and the ability to pause scrolling when hovering over with the mouse. Lastly, the 'setInterval' JS function (basically a timer) is wired up to call the 'MoveScrollingMarquee' method on interval. This method when called in turn moves the entire 'DIV' with text to the left by the step amount specified. The end result is a scrolling marquee!

Now, let’s move onto the code behind that will actually populate the marquee. Now the code I will provide simply creates a DataTable and populates it with City names and their respective temperatures. This entry is not going to dive into how to call a database and build up a custom object, list of objects ADO.NET type, entity, etc.; I assume most already know how to do this. So for my example I am going to build up some static data, but certainly you would extract this data dynamically from the database and iterate through it as I do to build out the 'InnerHTML' that will be set on our server side 'DIV' control we added to the page earlier.

This code example also does everything directly behind the page which is also not a great practice and does not follow any n-layer or design pattern guidelines. This code would need to be farmed out to its respective spots within the application, but again that is not the purpose of this entry. So below is the code needed to build the elements that go into the scrolling marquee:



Imports System.IO

Partial Public Class WebForm1
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

'This is where you would make calls to get a business object, list of objects, DataTable, DataSet, etc.
'Since that is not the purpose of this entry, I will just manually build out a DataTable with (2)
'columns: City and Temperature (of coarse it could be anything, and formatted however needed)
Dim dt As New DataTable()
dt.Columns.Add("City")
dt.Columns.Add("Temperature")

'Now add some dummy values
Dim dr As DataRow
dr = dt.NewRow()
dr("City") = "New York"
dr("Temperature") = "50"
dt.Rows.Add(dr)
dr = dt.NewRow()
dr("City") = "Miami"
dr("Temperature") = "75"
dt.Rows.Add(dr)
dr = dt.NewRow()
dr("City") = "San Diego"
dr("Temperature") = "60"
dt.Rows.Add(dr)
dr = dt.NewRow()
dr("City") = "Chicago"
dr("Temperature") = "45"
dt.Rows.Add(dr)

'Call a local method that will build up the HTML to be assigned to our server sode DIV control named 'ScrollingMarquee':
BuildScrollingMarquee(dt)

End Sub

Private Sub BuildScrollingMarquee(ByVal MarqueeValues As DataTable)

Dim sb As New StringBuilder

'Begin the HTML formatting by starting the DIV tag; the class name must be 'MarqueeTextObj' so it is recognized by JS
sb.Append("
")

'Loop through each datarow in the DataTable passed in, and extract and append the data to the StringBuilder.
'The format for the HTML should be City: [CityName] - Temperature: [Temperature Value]...
For Each dr As DataRow In MarqueeValues.Rows
'Add the string to the StringBuilder with HTML tags
sb.Append("City: " & dr("City").ToString() & " - Temperature: " & dr("Temperature").ToString() & "; ")
Next

sb.Append("
")

'*****Populate the scrolling marquee at the bottom of the page with data in the StrignBuilder formatted above*****
'Set the 'InnerHTML' of the server 'DIV' control
Me.ScrollingMarquee.InnerHtml = sb.ToString.Trim()

End Sub

Protected ReadOnly Property ScrollingMarqueeDivControlID() As String
'This returns the ControlID of the DIV containing the Horizontal scrolling marquee content.
'It will be exposed as a JS varibale in the source behind the page to be used within JavaScript.
Get
Me.EnsureChildControls()
Return Me.ScrollingMarquee.ClientID
End Get
End Property

End Class

The code behind shows that in Page_Load() we build up a DataTable with some data to be placed into the marquee. We then pass that DataTable to a local method that uses a StringBuilder to create properly formatted HTML. The 'BuildScrollingMarquee' method iterates through each DataRow in the passed in DataTable and uses its values to place into the StringBuilder. This is a spot where you can modify formatting, values, colors, you name it, to make the marquee text formatted as you wish. This in conjunction with the .css styles are the (2) main spots that will dictate styles and formatting if you need to make adjustments. Below is a picture of how the marquee looked as I formatted it:
So that's it! You now see how to combine JavaScript code with .NET code to dynamically populate and present a nice horizontal scrolling marquee. One last quick word - before posting back that something isn't working or there are issues, try running this code exactly as is in a new ASP.NET web form before modifying. This code has been tested and works, so take it a step at a time to get this code working before moving forward. As you being to become familiar with the code you can see where the many enhancements could be made to how the data is pulled, when, on what interval, styles, formatting, etc. This code hopefully gets you started with what you need by showing you the framework to dynamically populating a scrolling marquee.

0 comments:

Post a Comment