DHTML Slideshow with Fading Effect - Explanation

Compatibility

  • The fading effect does not work in Opera, Konqueror and Omniweb, because these browser do not support clipping
  • Older browsers show the pictures side by side
  • Explorer 5 on Mac doesn't support fading if you use the doctype
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
  • Mozilla doesn't support fading if you use XHTML or strict version of HTML 4

I have seen many slideshow scripts almost all 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.

Clipping is a 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>&nbsp;&nbsp;&nbsp;
<a href="javascript:changeSlide(1)">Next</a>&nbsp;&nbsp;&nbsp;
<a href="javascript:setFade(false)">No fade</a>&nbsp;&nbsp;&nbsp;
<a href="javascript:setFade(true)">Fade</a><br>
</div>

<div id="slide0" class="slide">
   <img src="slide0.jpg" alt="slide0" width="100" height="50">
</div>
<div id="slide1" class="slide">
   <img src="slide1.jpg" alt="slide1" width="100" height="50">
</div>
<div id="slide2" class="slide">
   <img src="slide2.jpg" alt="slide2" width="50" height="100">
</div>
<div id="slide3" class="slide">
   <img src="slide3.jpg" alt="slide3" width="100" height="50">
</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 of more than 10 pixels).

The JavaScript

The code is too long to be displayed on the page so it is located in an external file.

Explanation

The HTML

First the controls are set using anchors.

<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 wrapped in a div because NN 4 considers only div's and span's for layers, besides proprietary tags. The case is that NN 4 can only set visibility/clipping of a layer. The class="slide" is for 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;
}

#slide0 {
   visibility : visible;
}

First the class definition for all the slides. It sets the position to absolute to get the impression that the next slides comes from below the previous (not to have the user scroll down the page to see the next slide). visibility : hidden prevents the browser from visializing the slides on the screen, while still reserving the space needed. We set also the top and left coordinates of the slides (px is short for pixels).

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 by Peter Paul-Koch. I consider them self explaining, but for the uninitiated:

  • 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 result is 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 that won't get the hell out of you just to write a DHTML cross-browser code. What it does is to check what DOM is supported and according to the result create an object and stuff its obj and style properties with references to the style rules applied to the layer, and its HTML properties too.
  • The visib function triggers the visibility of a layer and is used basicly to ease the programmer not having to call the getObj 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 easier non-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. The setFade() 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 the fade or no fade slideshow.

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;
}

This is the function that I use to change the current slide displayed. If 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 required to determine whether after the fade a next one should be invoked. Then the layers clipping is set to no-clipping by the prepLayer function (the true parameter), and the fadeLayer function executed sets the global variables which 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 whether to be executed or not. Then it creates object x which refers to the layer manipulated, when it comes 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 have string indices, not only numbers to determine whether this layer's size has been determined before. If not set the lyrheight 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 you don't do so NN 4 will get a constantly reducing image size for your slides until they cannot be much longer visible. If the images has been shown before it just 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 or faded 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 the clip.top/right/bottom/left properties and set it's visibility to show(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 or fade 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 this layer scroll script, and I got the idea of the compatibility and decided to make a version in which the fading can be switched off. The thing is that the browser assume the pixels as default measure units, and I can't really tell you whether you 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 and then invoke the 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 is running it stops it.

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. Then we check whether the clipping is off the image, if it is we check whether it is fade out. When fade out we hide the slide, otherwise sometimes there are stripes that stay visible and distract the whole slideshow. If the firstFade variable is true we execute the nextFade function which fades in the next slide. In both case of fading we end the function, preventing the setTimeout invocation which would result in an infinite loop. The next thing to do is to actually apply the calculated clipping area to the layer, there are differences in the DOM 1 / IE 4 way and the NN 4 (described above in the prepLyr 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 looping the fading in effect and invokes the prepLyr function with parameters the next slide and false 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 the clipping area should get bigger, and a delay of 50 milliseconds to widen the area.

Comments

Slideshow

I was looking for a simple well written slide show program -- and found it in your descriptions.Thanks!Peter

Thanks

Thank you for the good words, there's also a live demo of the slideshow running at this site.

Slideshow with Fading Effect

This is cool and very informative. Write some more, please.

center?

hey, I'm trying to use your script and get the (variable width) elements to center all contents of the div (I'm acutally using it for more than photos). I can get the first one to center if I turn absolute positionig off of the "slide" style, but the slide show functions don't work.
I'm sorry, this is probably simple but i'm pretty new to DHTML stuff (btw, I don't care about the fading. It would be a nice bonus, but centering is more important).
Any ideas?

Try a different method

If you want to center the images but don't want the fading you can do it like the usual rollovers. You know how to do rollovers, right?

Error with the script

Hi martin,
I have error running the script. Can you teach me on how to install it properly step by step?
One more question, this sliding only show bout images...does it support sound and display content of an attachment as well...attachement here meant here is a text file.

No more error

hi again martin, i manage to get the script working. The only problem now is I'm not sure or can said don't know how to pass value into the script that do the sliding images. The value i mean here is the "slide" variable where you declare how many slides are there. I would like to have a dynamic one such as before displaying that page where the sliding image resides, user can select how many pictures they wan to view. Then this number selected is pass to the script to create an array of slide[number selected in previous page]
how can i do that?

Copyright

I notice that the last line on the SlideShow page refers to copyright. Do I need a release from the code
author(s) or an attribute line to use the code?

It does in Mozilla Firefox

Your Script works in Mozilla Firefox 0.8 Linux. Great!