One of the questions I often get is how to make a table where the body is scrollable. This is possible by setting the CSS2 property overflow to auto or scroll for the TBODY element. However Internet Explorer 5.5 or earlier (I have no idea about IE6) does not support the overflow property for the TBODY element so we'll have to do it in some other way.

Scrolling in IE DOM

I thought that the best way to create a scrollable table was to first create a general script that can synchronize the scroll position between two containers.

The secret behind scrolling is to know what properties and methods to use. IE exposes all the properties you need to work with scrolling. I've summarized the ones we will use below:

Properties:
scrollLeft The distance from the left edge of the content that the viewport is at.
scrollTop The distance from the top edge of the content that the viewport is at.
scrollWidth The width of the content. Notice that if this is bigger than the viewport then horizontal scrolling is possible.
scrollHeight The height of the content. Notice that if this is bigger than the viewport then vertical scrolling is possible.
Style Properties:
overflow This property describe what to do when the content is to big for the viewport. There are three possible values and these are:
hidden - No scroll bars are shown
auto - Show scroll bars if needed
scroll - Shows the scroll bars even if they are not needed
overflow-x IE5 includes an proprietary property that allows you to set the overflow value for horizontal overflow.
overflow-y IE5 includes an proprietary property that allows you to set the overflow value for vertical overflow.
Events:
scroll This event is triggered when the scrollLeft or scrollTop is changed. These change when a user scrolls the container or when a script changes these values.

Synchronizing

The goal here is to copy the scrollLeft and scrollTop value from one container to another. This needs to be done every time these values change and fortunately IE allows us to listen to these changes by attaching an event (unlike Netscape Navigator 4.x and Mozilla who don't have a scroll event).

In this script we are using the IE5 specific method called attachEvent and detachEvent to easily allow more than one listener to the scroll event. We first create a method that adds an event listener to a container and when the event occurs the scroll properties are updated.

function addScrollSync(fromElement, toElement, direction) {
   removeScrollSynchronization(fromElement);

   fromElement._syncScroll = getOnScrollFunction(fromElement);
   fromElement._syncDirection = direction;
   fromElement._syncTo = toElement;
   toElement.attachEvent("onscroll", fromElement._syncScroll);
}

This function first removes the scroll synchronization because it currently only supports synchronization from one other container. Then it sets some expando properties on the element and finally it attaches an event listener to the toElement. The function getOnScrollFunction returns a function that is used in the onscroll event of the toElement. (The reason why it is bounded to the element is that when detaching an event you need to pass along a reference to the same function.)

function getOnScrollFunction(oElement) {
   return function () {
      if (oElement._syncDirection == "horizontal" || oElement._syncDirection == "both")
         oElement.scrollLeft = event.srcElement.scrollLeft;
      if (oElement._syncDirection == "vertical" || oElement._syncDirection == "both")
         oElement.scrollTop = event.srcElement.scrollTop;
   };
}

Here you see that the direction can be "horizontal", "vertical" or "both". Notice how the argument is encapsulated in the returned function.

To remove the synchronized scrolling all we have to is to detach the event listener. We also want to reset the expando properties we added.

function removeScrollSync(fromElement) {
   if (fromElement._syncTo != null)
      fromElement._syncTo.detachEvent("onscroll", fromElement._syncScroll);

   fromElement._syncTo = null;;
   fromElement._syncScroll = null;
   fromElement._syncDirection = null;
}

Setting up the page

First we include the scripts above using a script tag with a src attribute:

<script type="text/ecmascript" src="syncscroll.js"></script>

Then we should set up the containers. These can be almost any element (table elements excluded) and should have a width and height to force the content to be bigger than the viewport to force scrolling. Below I'll do this is in a style block but it can be done in a lot of different ways.

#div1 {
	border: 1px solid black;
	padding: 2px;
	overflow: auto;
	width: 100px;
	height: 100px;
}

#div2 {
	border: 1px solid black;
	padding: 2px;
	overflow: hidden;
	width: 100px;
	height: 100px;
}

Then you should create your containers.

<div id="div1">
   Fill container...
</div>

<div id="div2">
   Fill container...
   (This one will be synchronized with the one above)
</div>

Then you need to call addScrollSync. This can be done as soon as both elements are available.

addScrollSync(document.getElementById("div2"), document.getElementById("div1"), "both");

Demo

I've put together a demo of this. The demo contains 4 divs where one has scroll bars and the other three synchronize their scroll values with the first one.

Since we've used IE5 specific code we can make the script a LOT easier to use by creating a behavior version.


Synchronized Scroll
Demo without behavior
Behavior version
Behavior version demo
Scrollable Table
Scrollable Table Demo
Download this sample

Author: Erik Arvidsson