Main Page Content
How To Create A Dhtml Slideshow With Fading Effect
Compatibility
- This script does not work in version 3 or lower browsers
- The fading effect does not work in Opera 5, Konqueror and Omniweb, because these browser don't support clipping
- Version 3 or lower browsers should just see the photos side by side and down to the end of the page
I have seen many slideshow scripts almost all of which support version 3 browsers but require
equal sized photos. My script does not require equal sized photos at all.In addition it also adds a fading effect( I can't quite remember is that called doors or only the horizontal version is).Clipping is a very powerful CSS property. It tells the browser what area of the visual element should be displayed, as far as I know only the
rect area( rectangle) is supported by now. You can produce amazing fade effects dynamically changing its value.The script
The HTML
<h1>My DHTML slideshow gallery</h1><div align="center"><a href="javascript:changeSlide(-1)">Previous</a> <a href="javascript:changeSlide(1)">Next</a> <a href="javascript:setFade(false)">No fade</a> <a href="javascript:setFade(true)">Fade</a><br></div> <div id="slide0" class="slide"><img src="images/slide0.jpg" alt="slide0" width="100" height="50" border="0"></div> <div id="slide1" class="slide"><img src="images/slide1.jpg" alt="slide1" width="100" height="50" border="0"></div> <div id="slide2" class="slide"><img src="images/slide2.jpg" alt="slide2" width="50" height="100" border="0"></div> <div id="slide3" class="slide"><img src="images/slide3.jpg" alt="slide3" width="100" height="50" border="0"></div> <div id="slide4" class="slide"><img src="images/slide4.jpg" alt="slide4" width="50" height="100" border="0"></div> <div id="slide5" class="slide"><img src="images/slide5.jpg" alt="slide5" width="100" height="50" border="0"></div> <div id="slide6" class="slide"><img src="images/slide6.jpg" alt="slide6" width="50" height="100" border="0"></div> <div id="slide7" class="slide"><img src="images/slide7.jpg" alt="slide7" width="100" height="50" border="0"></div>
Simple but working. Of course you do not have to name your images slide0.jpg and so on.
The stylesheet
<style type="text/css">.slide { position : absolute; visibility : hidden; top : 200px; left : 50px;}#slide0 {
visibility : visible;}</style>
Maybe you are wondering why I didn't set the clipping in the stylesheet but left it to the JavaScript.
The answer is simple: Netscape Navigator 4 can get only the height/width of the block( actually the clipping area), when no clipping is set( apparently it doesn't get the height value properly, but I have never seen a difference more than 10 pixels).The JavaScript
<script type="text/javascript">var DHTML = (document.getElementById document.all document.layers);if ( !DHTML ) alert('Your browser is not capable of displaying DHTML');function getObj(name) { if (document.getElementById) { this.obj = document.getElementById(name); this.style = document.getElementById(name).style; } else if (document.all) { this.obj = document.all[name]; this.style = document.all[name].style; } else if (document.layers) { this.obj = document.layers[name]; this.style = document.layers[name]; }}function visib(objName, flag) {
x = new getObj(objName); x.style.visibility = (flag) ? 'visible' : 'hidden';}slides = new Array( // The id's of the slides 'slide0', 'slide1', 'slide2', 'slide3', 'slide4', 'slide5', 'slide6', 'slide7');var fadeOn = false;
function setFade(switchFade) {// Fade switch function if ( !fadeOn ) { prepLyr(slides[curImg], true); } else { // No fade stopFade(); for ( var i = 0; i < 8; i++ ) { prepLyr(slides[i], true); if ( slides[i] != slides[curImg] ) visib(slides[i], false); } } fadeOn = switchFade;}var curImg = 0; // index of the array entry
var lastImg = 0;function changeSlide ( change ) { if (!DHTML) return; curImg += change; if ( curImg < 0 ) curImg = slides.length-1; else if ( curImg >= slides.length ) curImg = 0; if ( fadeOn ) { firstFade = true; prepLyr(slides[lastImg], true ); fadeLayer(slides[lastImg], 10, 50); } else { visib(slides[lastImg], false); visib(slides[curImg], true); } lastImg = curImg;}var firstFade = true;
function nextFade() { firstFade = false; prepLyr(slides[curImg], false); fadeLayer(slides[curImg], -10, 50);}var clipTop = 0;
var clipWidth = 150;var clipBottom = 0;var lyrheight = 0;var time,amount,theTime,middle;var slideSize = new Array()function prepLyr(lyr, vis) {
if (!DHTML) return; x = new getObj( lyr ); if (document.layers) { if ( !slideSize[lyr] ) { lyrheight = x.style.clip.bottom; clipWidth = x.style.clip.right; slideSize[lyr] = lyrheight + 'x' + clipWidth; } else { lyrheight = parseInt(slideSize[lyr]); clipWidth = slideSize[lyr].substr(slideSize[lyr].indexOf('x')+1); } if ( vis ) { clipTop = 0; middle = Math.round(lyrheight/2); clipBottom = lyrheight; } else { middle = Math.round(lyrheight/2); clipBottom = middle; clipTop = middle; } x.style.clip.top = clipTop; x.style.clip.left = 0; x.style.clip.right = clipWidth; x.style.clip.bottom = clipBottom; x.style.visibility = 'show'; } else if (document.getElementById document.all) { lyrheight = x.obj.offsetHeight; clipWidth = x.obj.offsetWidth; if ( vis ) { clipTop = 0; middle = Math.round(lyrheight/2); clipBottom = lyrheight; } else { middle = Math.round(lyrheight/2); clipBottom = middle; clipTop = middle; } x.style.clip = 'rect('+clipTop+' '+clipWidth+' '+ clipBottom +' 0)'; visib(lyr, true); }}function fadeLayer(layername, amt, tim) {
if (!DHTML) return; thelayer = new getObj( layername ); if (!thelayer) return; amount = amt; theTime = tim; realFade();}function stopFade() {
if (time) clearTimeout(time);}function realFade() {
clipTop += amount; clipBottom -= amount; if (clipTop < 0 clipBottom > lyrheight clipTop > middle) { if ( clipTop > middle ) thelayer.style.visibility = 'hidden'; if ( firstFade ) nextFade(); return; } if (document.getElementById document.all) { clipstring = 'rect('+clipTop+' '+clipWidth+' '+clipBottom+' 0)' thelayer.style.clip = clipstring; } else if (document.layers) { thelayer.style.clip.top = clipTop; thelayer.style.clip.bottom = clipBottom; } time = setTimeout('realFade()',theTime);}</script>
Explanation
The HTML
First the controls are set using anchors( I also thought of having a form with a checkbox for the fade
effect but the slideshow was to be included in a layer and it was to be a problem with NN 4).I have never liked coding for NN 4 browsers but unfortunately I have to do so because their userscannot be simply discarded off your site. Not that some should but in such a case it is not a matter (for abrowser market share stats (free) go to www.webreview.com/browsers/browser_faq.shtml).<div id="slide0" class="slide"><img src="images/slide0.jpg" alt="slide0" width="100"height="50" border="0"></div>
This sets the first slide of your DHTML slideshow. The img
should be wraped in a div
div
's and span
's ( I think) for layers, not counting proprietarytags. The case is that NN 4 can only set visibility/clipping of a layer. The class="slide"
isfor setting the position
and visibility
of all slides, and the id
is to identify the needed slide to be viewed.The stylesheet
.slide { position : absolute; visibility : hidden; top : 200px; left : 50px;}First the class definition for all the slides. It sets the#slide0 {
visibility : visible;}
position
to absolute
to get the impression that the next slides comes from below the previous (not to have the user scroll down thepage to see the next slide). Visibility : hidden
prevents the browser from visializing theslides on the screen, while still reserving the space needed. We set also the top
andleft
coordinates of the slides (px being the pixel unit).The following style rule is to make only the first slide visible to the user.
The script
var DHTML = (document.getElementById document.all document.layers);if ( !DHTML ) alert('Your browser is not capable of displaying DHTML');function getObj(name) { if (document.getElementById) { this.obj = document.getElementById(name); this.style = document.getElementById(name).style; } else if (document.all) { this.obj = document.all[name]; this.style = document.all[name].style; } else if (document.layers) { this.obj = document.layers[name]; this.style = document.layers[name]; }}function visib(objName, flag) { // triggers layer visibility
x = new getObj(objName); x.style.visibility = (flag) ? 'visible' : 'hidden';}
These are basic DHTML functions that I use for the slideshow. Actually I took them from
www.xs4all.nl/~ppk/js/I consider them self explaining, but for all of you who never had a chance to get involved with DHTML:- First we test for DHTML support( i.e. DOM1: the getElementById method, IE 4 the document.all collection,NN 4 the document.layers array). If there is such a method/array its toBoolean converted resultis true otherwise it is false, and if all evaluate to false that means that the browser does not support DHTML.
- Having the DHTML variable, described above we check if the browser does not support DHTML,and if this is so we inform the users that their browser won't show the slideshow properly.
- The
getObj
function is used to handle that browser incompatibilities in a way thatwon't get the hell out of you just to write a DHTML cross-browser code. What it does is to checkwhat DOM is supported and according to the result create an object and stuff itsobj
andstyle
properties with references to the style rules applied to the layer, and itsHTML properties too. - The
visib
function triggers the visibility of a layer and is used basicly to ease theprogrammer not having to call thegetObj
function by humself.
Only to note that in NN 4 if you want to get a reference to a sublayer you have to use
document.layers['parentLayer'].document.layers['subLayer']
instead of the easiernon-changing way of the other browsers.slides = new Array( // The id's of the slides 'slide0', 'slide1', 'slide2', 'slide3', 'slide4', 'slide5', 'slide6', 'slide7');var fadeOn = false;
function setFade(switchFade) {// Fade switch function if ( !fadeOn ) { prepLyr(slides[curImg], true); } else { // No fade stopFade(); for ( var i = 0; i < 8; i++ ) { prepLyr(slides[i], true); if ( slides[i] != slides[curImg] ) visib(slides[i], false); } } fadeOn = switchFade;}
First create an array of the id's of the slides, later used when switching from one image to another.
ThesetFade()
function is used to trigger fading effects on/off( initially no fading is set).The function uses some of the other functions to change the layers settings in a way that is sensible for thefade or no fade slideshow.var curImg = 0; // index of the array entryvar lastImg = 0;function changeSlide ( change ) { if (!DHTML) return; curImg += change; if ( curImg < 0 ) curImg = slides.length-1; else if ( curImg >= slides.length ) curImg = 0; if ( fadeOn ) { firstFade = true; prepLyr(slides[lastImg], true ); fadeLayer(slides[lastImg], 10, 50); } else { visib(slides[lastImg], false); visib(slides[curImg], true); } lastImg = curImg;}
This is the function that I use to change the current slide displayed. I no fade is set it only hides the
previous image and shows the next one, otherwise it sets the firstFade variable to true, which is requiredto determine wheter after the fade a next one should be invoked. Then the layers clipping is set to no-clippingby the prepLayer function( the true parameter), and the fadeLayer function executed sets the global variableswhich are later used by the realFade function.var clipTop, clipWidth, clipBottom, lyrheight;var time,amount,theTime,middle;var slideSize = new Array()function prepLyr(lyr, vis) {
if (!DHTML) return; x = new getObj( lyr ); if (document.layers) { if ( !slideSize[lyr] ) { lyrheight = x.style.clip.bottom; clipWidth = x.style.clip.right; slideSize[lyr] = lyrheight + 'x' + clipWidth; } else { lyrheight = parseInt(slideSize[lyr]); clipWidth = slideSize[lyr].substr(slideSize[lyr].indexOf('x')+1); } if ( vis ) { clipTop = 0; middle = Math.round(lyrheight/2); clipBottom = lyrheight; } else { middle = Math.round(lyrheight/2); clipBottom = middle; clipTop = middle; } x.style.clip.top = clipTop; x.style.clip.left = 0; x.style.clip.right = clipWidth; x.style.clip.bottom = clipBottom; x.style.visibility = 'show'; } else if (document.getElementById document.all) { lyrheight = x.obj.offsetHeight; clipWidth = x.obj.offsetWidth; if ( vis ) { clipTop = 0; middle = Math.round(lyrheight/2); clipBottom = lyrheight; } else { middle = Math.round(lyrheight/2); clipBottom = middle; clipTop = middle; } x.style.clip = 'rect('+clipTop+' '+clipWidth+' '+ clipBottom +' 0)'; visib(lyr, true); }}
First declaration of the global variables. The prepLyr function first tests the global variable
DHTML wheter to be executes or not. Then it creates object x which refers to the layer manipulated, when itcomes to the hard part of it. This is where there is no way to write a code that will be executed on all browsers,and especially great problems with NN 4 (not wondering). I use the fact that arrays in JavaScript can havestring indices, not only numbers to determine whether this layer's size has been determined before.If not set thelyrheight
to the clipping area's bottom (which should mean the image's height),and the clipWidth
to the image width. Then create an array entry with that data, because if youdon't do so NN 4 will get a constanlty reducing image size for your slides until they cannot be much longervisible. If the images has been shown before it justs reads the array entry to get these values.Then it is time to test the vis
parameter which tells us whether the image should be faded in orfaded out. When fading in we initially set the clippig area's top and bottom to be the middle of the image,otherwise the are the top and bottom coordinates respectively.When all these calculations have been made we actually apply the settings to the layer, in NN 4 it is
through theclip.top/right/bottom/left
properties and set it's visibility
toshow
( Netscape's proprietary value).Then we have finished NN 4 code and we check for DOM 1 or IE 4 support. The task is easier here because
no matter what clipping set we can always get the image height/width. Then we make the same test for fade in orfade out, and at the end apply the settings as a string to the clip property.Actually I first made a version of the slideshow with the units specified( px), but I didn't like it at all
because I couldn't get it to work in NN 4 and Opera 5. Then I found thislayer scroll script,and I got the idea of the compatibility and decided to make a version in which the fading can be swiched off.The thing is that the browser assume the pixels as default measure units, and I can't really tell you whetheryou should specify them.function fadeLayer(layername, amt, tim) { if (!DHTML) return; thelayer = new getObj( layername ); if (!thelayer) return; amount = amt; theTime = tim; realFade();}function stopFade() {
if (time) clearTimeout(time);}
The fadeLayer
function is used just to set the global variables to the parameters passed to it
realFade
function to do the hard work.The only thing that the stopFade
function does is that it checks the timer ID and if the timer
function realFade() { clipTop += amount; clipBottom -= amount; if (clipTop < 0 clipBottom > lyrheight clipTop > middle) { if ( clipTop > middle ) thelayer.style.visibility = 'hidden'; if ( firstFade ) nextFade(); return; } if (document.getElementById document.all) { clipstring = 'rect('+clipTop+' '+clipWidth+' '+clipBottom+' 0)' thelayer.style.clip = clipstring; } else if (document.layers) { thelayer.style.clip.top = clipTop; thelayer.style.clip.bottom = clipBottom; } time = setTimeout('realFade()',theTime);}var firstFade = true;
function nextFade() { firstFade = false; prepLyr(slides[curImg], false); fadeLayer(slides[curImg], -10, 50);}
The realFade
function adds the amount to the clipTop and subtracts it from the clipBottom.
firstFade
variable is true we execute the nextFade
function whichfades in the next slide. In both case of fading we end the function, preventing the setTimeout invokationwhich would result in an infinite loop. The next thing to do is to actually apply the calculated clippingarea to the layer, there are differences in the DOM 1 / IE 4 way and the NN 4 (described above in theprepLyr
function explanation). Afterwards we set a timeout that executes the same function again.The nextFade
function then sets the fisrtFade
variable to false, to prevent
prepLyr
function with parameters the next slide andfalse to initialize the image to fade in( clipping top and bottom to the middle). The realFade
function is then invoked, passing it the current slide, a change amount of -10 pixels, which means that theclipping area should get bigger, and a delay of 50 miliseconds to widen the area.