Wednesday, August 5, 2009

JavaScript and DHTML Text Scroller with progress bar

You can find a wide variety of DHTML text scrollers on internet but I have created an easy to use scroller with a lot of flexibility and a progress bar. This is cross-browser compatible scroller and you can use multiple scrollers on the same page without assigning separate id’s to them.

Here are some benefits of using this DHTML scroller:
  • Zero coding required.
  • You can use multiple scrollers on a single page without assigning id’s separately.
  • Easily modifiable CSS layout and works with different styles for different scrollers on the same page.
  • Works with any mouse event giving you the flexibility of automatic and manual scrolling.
  • Cross-Browser compatibility.

JavaScript DHTML Scroller

Here are the sample scrollbar images (right click and save): up.png down.png

The scrolling function can be called from mouse over event for automatic scrolling or mouse down event for manual scrolling. The scrolling function is passed an object to the main container of the scroller and rest of the elements are navigated from there. This scroller code follows a strict hierarchy of div tags, so if you want to modify this and use your own styling and node hierarchy then you will have to change the JavaScript code to reach for the proper child nodes. For testing you can always use alert(element.className) to verify if you are navigating to the correct nodes. Instead of using the firstChild or childNodes functions from DOM, I have created my own function to get the child element nodes because when navigating using the DOM functions FireFox browser treats white spaces as text nodes and gives inconsistent results, for example to get to the third Element Node under the container you can use getElementChildNode(container, 2) (index starts at 0).

JavaScript Code:
<script type="text/javascript">
//Developed by: Abhinay Rathore | web3o.blogspot.com
var SPEED = 2; //scroll speed in pixels
//Starts the scrolling
function startScroll(container, dir, timer) {
    var scroller = getElementChildNode(getElementChildNode(container, 0), 0);
    clearInterval(scroller.timer); //clear existing timer
    //get Progress Column div id
    var progress_column = getElementChildNode(container, 2);
    //alert(progress_column.className);
    var progress_style = getstyle(progress_column);
    var progress_bar = getElementChildNode(progress_column, 0); //get Progress bar id
    var progress_bar_style = getstyle(progress_bar); //get Progress bar style
    //calculate the Progress bar scrolling limit
    var progress_bar_limit = parseInt(progress_style.height.replace('px', '')) - parseInt(progress_bar_style.height.replace('px', ''));
    //get current top offset for the Progress bar (default 0px)
    progress_bar.style.top = progress_bar.style.top || '0px';
    var progress_bar_top = parseInt(progress_bar.style.top.replace('px', ''));
    //calculate Scroll limit
    var limit = scroller.offsetHeight - scroller.parentNode.offsetHeight;
    //calculate Progress bar speed
    var progress_speed = (progress_bar_limit / (limit / SPEED));
    //create new object of Progress class
    progress = new Progress(progress_bar_limit, progress_speed, progress_bar_top);
    //alert(progress_bar_top);
    if (dir == -1) { // UP
        limit = 0;
        progress.limit = 0;
        scroller.timer = setInterval(function() { scrollUp(scroller, limit, progress_bar, progress) }, timer);
    }
    else if (limit >= 0) // DOWN
        scroller.timer = setInterval(function() { scrollDown(scroller, limit, progress_bar, progress) }, timer);
}
function scrollUp(scroller, limit, progress_bar, progress) { // SCROLL UP
    var container = scroller.parentNode.parentNode;
    scroller.style.top = scroller.style.top || '0px';
    var top = scroller.style.top.replace('px', '');
    if (Math.abs(top) - limit <= SPEED) { //top end reached
        cancelScroll(container);
        scroller.style.top = limit + 'px';
        progress_bar.style.top = Math.round(progress.getLimit()) + 'px';
        progress.setValue(progress.getLimit());
    } else {
        scroller.style.top = parseInt(top) + SPEED + 'px';
        progress_bar.style.top = Math.round(progress.getValue()) + 'px';
        progress.setValue(progress.getValue() - progress.getSpeed());
    }
}
function scrollDown(scroller, limit, progress_bar, progress) { // SCROLL DOWN
    var container = scroller.parentNode.parentNode;
    scroller.style.top = scroller.style.top || '0px';
    var top = scroller.style.top.replace('px', '');
    if (limit - Math.abs(top) <= SPEED) { //botttom end reached
        cancelScroll(container);
        scroller.style.top = '-' + limit + 'px';
        progress_bar.style.top = Math.round(progress.getLimit()) + 'px';
        progress.setValue(progress.getLimit());
    } else {
        scroller.style.top = top - SPEED + 'px';
        progress_bar.style.top = Math.round(progress.getValue()) + 'px';
        progress.setValue(progress.getValue() + progress.getSpeed());
    }
}
// Cancel the scrolling on mouseout
function cancelScroll(container) {
    var scroller = getElementChildNode(getElementChildNode(container, 0), 0);
    clearTimeout(scroller.timer);
}
// Get Element Child node and specified index
// this is a fix for FireFox browser where firstChild and
// childNodes[] DOM functionalities don't work sometime
// as FireFox treats empty spaces as text nodes.
function getElementChildNode(node, index) {
    var element_node;
    var element_node_index = 0;
    var children = node.childNodes;
    for (var i = 0; i < children.length; i++) {
        if (children[i].nodeType == 1) {
            element_node = children[i];
            if (element_node_index == index) break;
            else element_node_index++;
        }
    }
    return element_node;
}
// Class for Progress bar
function Progress(limit, speed, value) {
    this.limit = limit;
    this.speed = speed;
    this.value = value;
    this.getLimit = function() { return this.limit; };
    this.getSpeed = function() { return this.speed; };
    this.getValue = function() { return this.value; };
    this.setValue = function(value) { this.value = value; };
}
// Get CSS Style properties
function getstyle(id) {
    var myStyle = id.currentStyle;
    if (navigator.appName == "Microsoft Internet Explorer")
        myStyle = id.currentStyle;
    else myStyle = document.defaultView.getComputedStyle(id, null);
    return myStyle;
}
</script>

I have used a very basic set of styling for the scrolling window. You can modify the CSS to match the color and styling for your website. Some of the things in the CSS should not be changed such as the relative positioning of the scroll_content and progress_bar div tags. If you wish to change the hierarchy under the scrolling container, make sure you pass the correct objects to the functions, for more help on the node hierarchy read the paragraph following the CSS code very carefully.

CSS Code:
<style type="text/css">
<!--
.container {
      height: 300px; width: 500px;
      border: 1px solid #000;
      padding: 5px;
}
.scroll_window {
      width: 475px; height: 300px;
      overflow: hidden; float: left;
      margin-right: 5px;
}
.scroll_content {
      position: relative;
      font-family: Verdana, Geneva, sans-serif;
      font-size: small; text-align: justify;
}
.up, .down {
      float: left; height: 20px; width: 20px;
}
.progress_column {
      float: left; height: 260px; width: 20px;
}
.progress_bar {
      height: 20px; width: 20px;
      position: relative;
      background-color: #CCC;
}
-->
</style>

This scroller follows a strict hierarchy of div tags to make it a zero-coding tool. On mouse events, three arguments are passed to the startScroll function. First is the object to the main container from the image nodes by using DOM navigation function parentNode as this.parentNode.parentNode, second argument is the scrolling direction (-1 = up, 1 = down) and third argument is to set the scrolling speed. If your scrolling content is very large then you can set the scrolling speed to to higher number.

Here is the hierarchy of nodes in Scroller HTML DOM:
    container
            |_____scroll_window
            |                 |____scroll_content
            |_____up
            |      |____img
            |_____progress_column
            |                   |____progress_bar
            |_____down
                     |____img

You can have multiple copies of this scroller HTML code on your page without worrying about the calling function. The content for the scroller goes under the  scroll_content div tag.

HTML Code:
<div class="container">
    <div class="scroll_window">
        <div class="scroll_content">
            Your Content goes here!
        </div>
    </div>
<div class="up"><img onmouseover="startScroll(this.parentNode.parentNode, -1, 1)" onmouseout="cancelScroll(this.parentNode.parentNode)" src="up.png" alt="UP" /></div>
<div class="progress_column"><div class="progress_bar"></div></div>
<div class="down"><img onmouseover="startScroll(this.parentNode.parentNode, 1, 1)" onmouseout="cancelScroll(this.parentNode.parentNode)" src="down.png" alt="UP" /></div>
</div>

Feel free to use this DHTML scroller and modify it as you wish. But please do include a reference to my blog.

Technorati Tags: ,,

1 comment:

Thanks a lot for your valuable comments :)