<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
See this script in real live action at the stories pages of the
World Press Photo site I coded.
It is based on a script by my colleague
Tijs Teulings.
Note: In the script below I use x.style.clip.bottom
to read out
the height of the scrolling layer in Netscape 4. Only after that I set the clipping styles.
I have meanwhile found out that x.obj.document.height
also gives the layer height
in Netscape 4. If you use it, you can put a clip
declaration in the style sheet
and thus keep all styles in one central file.
I haven't yet updated the script to include these new discoveries.
On this page I present my version of the layer scrolling script. It is much simpler than most such scripts because I do not use a containing layer for the scrolling layer.
In functionality the scrolling layer is similar to an IFRAME, but in addition it works in Netscape 4. Furthermore sometimes the design calls for another scrolling solution than scrollbars. This script uses two links for the scrolling.
This is an example of the scrolling layer. You can scroll it by using the Up and Down links below. Of course it has to be in an absolute position on the page, which doesn't fit in my design, so you can also make it visible and invisible.
To see the example, first make the layer visible (later you can make it invisible again), then scroll the layer up or down.
Contrary to most layer scrolling scripts on the Internet, this script needs only one layer, the actual scrolling layer. The principle is very simple. The layer is positioned absolutely and it is clipped so that only part of it is shown.
Clipping is a very useful CSS property that allows you to define which part of the layer should be visible. We're going to use it to show only that part of the layer that should be visible. The general syntax is:
clip: rect(top right bottom left)
So to clip a part of a layer so that only this part is visible, do something like:
some_element { position: absolute; clip: rect(10 110 210 10); }
where the four values are the borders of the clipped area, in pixels relative to the layer itself. So
rect(10 110 210 10)
would give a clipped area of 100 pixels wide and 200 pixels high starting
10 pixels from the left and from the top border of the layer.
Please note that the element must have an absolute position or the clipping won't work.
However, in this case we do not set the clipping in the style sheet! We'll set it in the preparation script for reasons explained below.
So by using clipping we set the initial position of the scrolling layer, the visible part is the part that is clipped.
-----------------
|1 | Other content
|2 Visible part | Other content
|3 | Other content
|4 | Other content
|5 |
|6 |
|7 Invisible |
|8 part |
|9 |
|10 |
-----------------
When the layer is scrolled, its top is raised and simultaneously the clipped area is moved so that the clipped part remains at its original position. If the effect is smooth enough, the layer appears to remain at the same position while the content appears to be scrolling. (Unfortunately in Netscape 6 the effect is not smooth enough.)
----------------- |1 Invisible | |2 part | |3 | Other content |4 Visible part | Other content |5 | Other content |6 | Other content |7 | |8 Invisible | |9 part | |10 | -----------------
The most tricky bit is knowing when to stop: how high is the layer? It should stop scrolling when reaching this position:
-----------------
|1 |
|2 |
|3 Invisible |
|4 part |
|5 |
|6 |
|7 | Other content
|8 Visible part | Other content
|9 | Other content
|10 | Other content
-----------------
Needless to say, this script solves that problem.
For this script you need the DHTML micro-API function. It is called several times.
var clipTop = 0; var clipWidth = 170; var clipBottom = 50; var topper = 300; var lyrheight = 0; var time,amount,theTime,theHeight,DHTML; function prepLyr() { DHTML = (document.getElementById || document.all || document.layers) if (!DHTML) return; var x = new getObj('example'); if (document.layers) { lyrheight = x.style.clip.bottom; lyrheight += 20; x.style.clip.top = clipTop; x.style.clip.left = 0; x.style.clip.right = clipWidth; x.style.clip.bottom = clipBottom; } else if (document.getElementById || document.all) { lyrheight = x.obj.offsetHeight; x.style.clip = 'rect('+clipTop+' '+clipWidth+' '+clipBottom+' 0)' } } function scrollayer(layername,amt,tim) { if (!DHTML) return; thelayer = new getObj(layername); if (!thelayer) return; amount = amt; theTime = tim; realscroll(); } function realscroll() { if (!DHTML) return; clipTop += amount; clipBottom += amount; topper -= amount; if (clipTop < 0 || clipBottom > lyrheight) { clipTop -= amount; clipBottom -= amount; topper += amount; return; } if (document.getElementById || document.all) { clipstring = 'rect('+clipTop+' '+clipWidth+' '+clipBottom+' 0)' thelayer.style.clip = clipstring; thelayer.style.top = topper; } else if (document.layers) { thelayer.style.clip.top = clipTop; thelayer.style.clip.bottom = clipBottom; thelayer.style.top = topper; } time = setTimeout('realscroll()',theTime); } function stopScroll() { if (time) clearTimeout(time); }
Call prepLyr()
like
<BODY onLoad="prepLyr()">
and set events for the scrolling. I chose onMouseOver and onMouseOut, of course you can use other events.
Scroll <A HREF="#" onMouseOver="scrollayer('example',-10,100)" onMouseOut="stopScroll()">up</A> or <A HREF="#" onMouseOver="scrollayer('example',10,100)" onMouseOut="stopScroll()">down</A>
It is very important that you do not set the clipping in the style sheet. That's because Netscape 4 needs the whole layer, unclipped, to find out how high it is. We set the clipping later on.
First we define some variables:
var clipTop = 0; var clipWidth = 170; var clipBottom = 50; var topper = 300; var lyrheight = 0; var time,amount,theTime,theHeight,DHTML;
These variables will keep track of the current position and clipping of the layer. We set their
default values. clipTop
and clipBottom
are set to 0 and 50, so that an area of
50 pixels high is clipped out of the layer (and thus visible to the user).
The layer is 150 pixels wide. We set clipWidth
to 170 pixels to allow for browser
differences in the box model. This value does not change during the script but it's used several times.
topper
keeps track of the top of the entire layer during the scrolling. Default
value is the same as that of the top
property in the style sheet, 300 pixels in this case.
lyrheight
will contain the total height of the layer later on, while the other variables
are used for minor things in the script.
onLoad we call the function prepLyr()
which will prepare the layer for scrolling. First
we find out if the browser supports DHTML, if it doesn't we end the function.
function prepLyr() { DHTML = (document.getElementById || document.all || document.layers) if (!DHTML) return;
Then we load the layer into variable x
by means of the
DHTML micro-API.
var x = new getObj('example');
The most important task of the function is to find out how high the layer is and set the clipping afterwards. Needless to say, Netscape 4 needs different code than the other browsers.
if (document.layers) {
The interesting bit is reading out the height. Contrary to what you'd expect, the height of
the layer is not x.style.height
, Instead, in its infinite wisdom Netscape has seen fit
to hide this information in x.style.clip.bottom
, or the bottom border of the current clipping!
(The properties of clip
are also a Netscape proprietary extension, by the way).
Netscape 4 sees the layer as being already clipped, the clipped part being the entire layer. So the bottom of the clipped part is also the bottom of the layer. This is the reason why we cannot set the clipping in the style sheet, it would destroy this important information before we can read it out.
So we read out the layer height and add a bit to it (otherwise the scrolling would stop slightly sooner than we'd wish; set this value experimentally):
lyrheight = x.style.clip.bottom; lyrheight += 20;
Then it's time to set the clipping by using Netscape's proprietary properties of clip
.
Changing the value of x.style.clip
doesn't have any effect and in very
old Netscape 4's this property is even read-only, so that writing to it would produce errors.
x.style.clip.top = clipTop; x.style.clip.left = 0; x.style.clip.right = clipWidth; x.style.clip.bottom = clipBottom; }
Then for the other browsers
else if (document.getElementById || document.all) {
Here the height of the layer is x.obj.offsetHeight
so we read it out and then
set the clipping of the layer by
lyrheight = x.obj.offsetHeight; x.style.clip = 'rect('+clipTop+' '+clipWidth+' '+clipBottom+' 0)' } }
Now the layer is ready for scrolling and we wait for user input.
When the user mouses over the Up and Down links, this function is called. It initalizes the
scrolling. You give it some information: which layer should be scrolled (layername
),
by what amount should it be scrolled (amt
, in pixels) and after which time should
it be scrolled again (tim
, in milliseconds).
<A HREF="#" onMouseOver="scrollayer('example',-10,100)" onMouseOut="stopScroll()">up</A> function scrollayer(layername,amt,tim) {
If DHTML is not supported, end the function.
if (!DHTML) return;
Put the layer to be scrolled in the object thelayer
. If for some reason it does
not exist, end the function.
thelayer = new getObj(layername); if (!thelayer) return;
Set amount
and theTime
, the variables the main function uses for determining
the exact scrolling amount and time.
amount = amt; theTime = tim;
Then call the actual scrolling function.
realscroll(); }
This is the function that actually does the work. It moves and re-clips the layer once, then calls itself again.
First of all, if DHTML is not supported, end the function.
function realscroll() { if (!DHTML) return;
Change the variables that define the clipped area and the top of the layer by
amount
pixels.
clipTop += amount; clipBottom += amount; topper -= amount;
Then see if this would scroll the layer too far up or down by comparing clipTop
with 0 (it shouldn't be lower) and clipBottom
with lyrheight
(it shouldn't
be higher). If something is wrong, restore the original values to clipTop, clipBottom and topper
and end the function. The end of the layer has been reached.
if (clipTop < 0 || clipBottom > lyrheight) { clipTop -= amount; clipBottom -= amount; topper += amount; return; }
If the script makes it to here everything is OK and we should scroll the layer. This means
changing its top
property and its clip
property. First for all other browsers,
where we make a new clipstring
and write it into x.style.clip
:
if (document.getElementById || document.all) { clipstring = 'rect('+clipTop+' '+clipWidth+' '+clipBottom+' 0)' thelayer.style.clip = clipstring; thelayer.style.top = topper; }
then for Netscape 4, where of course we use the proprietary properties of clip
.
else if (document.layers) { thelayer.style.clip.top = clipTop; thelayer.style.clip.bottom = clipBottom; thelayer.style.top = topper; }
Finally, set a timeout so that in the amount of time defined the function is called again,
again scrolling the layer a little bit. We store this timeout in the variable time
.
time = setTimeout('realscroll()',theTime); }
One last tiny function that is called onMouseOut. It stops the scrolling by using the
clearTimeout
method on the variable time
. This means that the timeout is
cancelled and the scrolling stops. I check if the variable time
exists first, to prevent
problems in one browser (sorry, can't remember which one).
function stopScroll() { if (time) clearTimeout(time); }
That's it, the layer scrolls, delighting your users.