home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Previous Section Next Section

15.4 Creating a Slide Show

NN 6, IE 5.5

15.4.1 Problem

You want to deliver the equivalent of a PowerPoint presentation in a web browser.

15.4.2 Solution

There are dozens of ways to simulate a slide show, but this recipe is a simple, easy-to-implement solution for recent browsers. All of the content, style sheets, and scripts are contained in a single HTML page, whose code is shown in Example 15-2 in the Discussion.

To customize this slide show, place the content of each slide in its own div element with a class attribute set to slide. All of your slide div elements should be nested inside one master div element named slides. Keep the individual slide div elements in the same source code order as the presentation slide order.

You can also customize the style sheets that define the look of the slides. The format shown in Example 15-2 simulates a typical presentation software package's simple slide style.

15.4.3 Discussion

Example 15-2 shows the HTML page used for a slide show (with only one slide to save space). With the entire slide show arriving in a single document, there is no delay in navigating between slides.

Example 15-2. A slide show HTML page and scripts
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>DHTML Slide Show</title>
<style type="text/css">
#slides {font-family:Verdana, Arial, sans-serif; 
         position:absolute; 
         top:40px; 
         width:90%
        }
.slide {position:absolute; 
        top:0px; 
        left:0px; 
        display:none; 
        width:80%; 
        height:500px; 
        overflow:hidden; 
        background-color:#ccffcc; 
        font-size:18px; 
        padding:20px; 
        border:5px solid #ff9900; 
        margin:10%
       }
h1 {text-align:right; 
    padding-right:10%
   }
h2 {font-size:36px; 
    text-align:center
   }
li {list-style-image:url(end.gif); 
    list-style-position:outside
   }
hr {width:60%; 
    height:5px; 
    background-color:#ff9900
   }
#titleBar {width:100%; 
           height:10px
          }
body {background-color:#339966}
#controller {position:absolute; 
             top:30px; 
             left:10%
            }
</style>
<script type="text/javascript" src="DHTMLapi.js"></script>
<script type="text/javascript">
// Array of all slides
var allSlides;
// Slide counter
var currSlide = -1;
   
// Set global with array of slide elements
function getAllSlides( ) {
    var allChildren = document.getElementById("slides").childNodes;
    var slideElems = new Array( );
    for (var i = 0; i < allChildren.length; i++) {
        if (allChildren[i].nodeType =  = 1) {
             slideElems[slideElems.length] = allChildren[i];
        }
    }
    allSlides = slideElems;
}
   
// Set pixel heights of slide elements to fit window
function setHeights( ) {
    for (var i = 0; i < allSlides.length; i++) {
        allSlides[i].style.height = getInsideWindowHeight( ) - 200 + "px";
    }
}
   
// Advance to next slide
function next( ) {
    if (currSlide < 0) {
        allSlides[++currSlide].style.display = "block";
    } else if (currSlide < allSlides.length - 1) {
        allSlides[currSlide].style.display = "none";
        allSlides[++currSlide].style.display = "block";
    } else if (currSlide =  = allSlides.length - 1) {
        allSlides[currSlide++].style.display = "none";
    }
}
   
// Go to previous slide
function prev( ) {
    if (currSlide > allSlides.length - 1) {
        allSlides[--currSlide].style.display = "block";
    } else if (currSlide > 0) {
        allSlides[currSlide].style.display = "none";
        allSlides[--currSlide].style.display = "block";
    } else if (currSlide =  = 0) {
        allSlides[currSlide--].style.display = "none";
    }
}
   
// Initialize slide show
function initSlides( ) {
    getAllSlides( );
    setHeights( );
}
</script>
</head>
<body onload="initDHTMLAPI( ); initSlides( )" onresize="setHeights( )">
<h1>U.S. Bill of Rights</h1>
<hr id="titleBar" />
<div id="controller">
<form>
<input type="button" value="Prev" onclick="prev( )" />
<input type="button" value="Next" onclick="next( )" />
</form>
</div>
   
<div id="slides">
   
<div id="slide1" class="slide">
<h2>ARTICLE I</h2>
<hr />
<p>
Congress shall make no law respecting an establishment of religion, or prohibiting 
the free exercise thereof; or abridging the freedom of speech, or of the press; or 
the right of the people peaceably to assemble, and to petition the government for 
a redress of grievances.
</p>
<ul>
<li>Note 1</li>
<li>Note 2</li>
<li>Note 3</li>
</ul>
</div>
...
   
</div>
</body>
</html>

While the script code works in IE 5 or later and NN 6 or later, the CSS specifications in this example require IE 5.5 or later on Windows to achieve the consistent look shown in Figure 15-1.

Figure 15-1. A slide show window
figs/jsdc_1501.gif

A lot of what a slide show looks like depends upon the CSS rules applied to a series of positioned div elements (of the slide class) nested inside one outer-positioned div element (with ID slides). Additional style rules control the look of elements within slides, such as headings, bullet lists, and horizontal rule dividers.

The HTML for the slide title area, controller buttons, and content has a straightforward structure. Controller buttons simply activate the prev( ) and next( ) functions to bring an adjacent slide into view. A style sheet rule positions the controller buttons where it is convenient within the rest of the design of the slides. The body's onload event handler invokes initializations for both the DHTML API (Recipe 13.3) and the slide show scripts.

This slide show works under script control by hiding and showing individual div elements representing the slides. The script, therefore, simply needs to keep track of which slide is showing and display either the next or previous slide in sequence. Rather than hardcoding IDs of the individual slides to establish the sequence, the scripts take advantage of the DOM node tree established for this document. Each slide is a child element node of an outer wrapper (whose ID is slides). The getAllSlides( ) function stores (in the allSlides global variable) an array reference to the slides in source code order. Note that it is invoked by the onload event handler because the div elements must load and render before they can be counted.

The zero-based index value of the array becomes the counter that helps keep track of the currently displayed slide. A global variable, currSlide, is initialized with a value of -1, to signify that no slide is showing and the first slide (index of 0) is next in the sequence. Both the next( ) and prev( ) functions rely on the currSlide value to determine the next or previous slide to show in sequence until a border condition is encountered—when the user has reached the start or end of the show. The next( ) and prev( ) functions need to branch to handle these conditions and manage the currSlide variable accordingly.

One unfinished piece of business is that to prevent the active area of the slide from changing height with each slide (based on content height), a fixed slide height is needed. But to keep the height in proportion to the browser window size needs some DHTML assistance. With the help of the DHTML API library from Recipe 13.3, the setHeights( ) function makes the adjustments to all of the slide div elements. Note that the setHeights( ) function is invoked both through the onload and onresize event handlers of the body element. This keeps the height in proper proportions in all circumstances.

The look and feel of a DHTML-based slide show has as many variations as there are web designers implementing the application. Most of the details shown for the style sheets in this recipe govern the appearance of this variation only. Size, color, and font choices are entirely up to you. The same goes for the controllers, implemented here as basic HTML form button controls. You may prefer clickable images, or you may not want any buttons at all, so that the slides advance by a click anywhere in the window. Adding the following code to the scripts just shown provides that functionality (except in IE on the Mac), along with a way to go backward by holding down the Shift key while clicking in the window:

function changeSlide(evt) {
    evt = (evt) ? evt : window.event;
    if (evt.shiftKey) {
        prev( );
    } else {
        next( );
    }
}
document.onclick=changeSlide;

One aspect of typical slide shows not covered in the previous code is the transition effect. While it is possible to script numerous cross-browser effects, such as a discarded slide moving off to the left while the new one floats in from the right, the use of this kind of animation is of variable smoothness and speed, depending on browser and operating environment. But Internet Explorer for Windows makes good use of its integration with the operating system to provide a large set of transition filters that are specified as part of the style sheet rules.

The catalog of transition effects is long, as is the range of details you can specify for most transitions (see Recipe 12.9). But as a preview, the following style sheet rule can be added to the .slide class definition in the previously shown CSS rules:

filter:progid:DXImageTransform.Microsoft.Iris(irisStyle="circle")

Next, modify the next( ) and prev( ) functions to apply and play the transitions when needed. For example, the next( ) function becomes:

function next( ) {
    var allSlides = getAllSlides( );
    if (currSlide < 0) {
        allSlides[++currSlide].filters[0].apply( );
        allSlides[currSlide].style.visibility = "visible";
        allSlides[currSlide].filters[0].play( );
    } else if (currSlide < allSlides.length - 1) {
        allSlides[currSlide].style.visibility = "hidden";
        allSlides[++currSlide].filters[0].apply( );
        allSlides[currSlide].style.visibility = "visible";
        allSlides[currSlide].filters[0].play( );
    } else if (currSlide =  = allSlides.length - 1) {
        allSlides[currSlide++].style.visibility = "hidden";
    }
}

To allow these transitions to work in IE for Windows without causing script errors in IE for the Mac or Netscape browsers, you'll need to lengthen the code a little more to accommodate all supporting browsers:

function next( ) {
    var allSlides = getAllSlides( );
    var nextSlide;
    if (currSlide < 0) {
        nextSlide = allSlides[++currSlide];
        if (nextSlide.filters) {
            nextSlide.filters[0].apply( );
        }
        nextSlide.style.visibility = "visible";
        if (nextSlide.filters) {
            nextSlide.filters[0].play( );
         }
    } else if (currSlide < allSlides.length - 1) {
        allSlides[currSlide].style.visibility = "hidden";
        nextSlide = allSlides[++currSlide];
        if (nextSlide.filters) {
            nextSlide.filters[0].apply( );
        }
        nextSlide.style.visibility = "visible";
        if (nextSlide.filters) {
            nextSlide.filters[0].play( );
       }
    } else if (currSlide =  = allSlides.length - 1) {
        allSlides[currSlide++].style.visibility = "hidden";
    }
}

Lastly, you can set up a slide show so that it automatically cycles through the slides, giving users a fixed amount of time to read each slide. Modify the next( ) function (you don't need prev( ) for this) to circle around when it reaches the end:

function next( ) {
    var allSlides = getAllSlides( );
    if (currSlide < 0) {
        allSlides[++currSlide].style.visibility = "visible";
    } else if (currSlide < allSlides.length - 1) {
        allSlides[currSlide].style.visibility = "hidden";
        allSlides[++currSlide].style.visibility = "visible";
    } else if (currSlide =  = allSlides.length - 1) {
        allSlides[currSlide++].style.visibility = "hidden";
        currSlide = -1;
        next( );
    }
}

Then use the onload event handler of the page to initiate a setInterval( ) call to next( ) every several seconds. Because the onload event handler shown earlier already performs an initialization in setHeights( ), you can enter the setInterval( ) call at the end of that function. But since that function is also invoked if the page is resized, you need to cancel the previous interval timer if the user resizes the window. Failure to do so starts another interval time, which gives the impression of speeding up the slide pacing. Therefore, preserve the interval identifier in a global variable:

var slideInterval;
   
function setHeights( ) {
    clearInterval(slideInterval);
    var allSlides = getAllSlides( );
    for (var i = 0; i < allSlides.length; i++) {
        allSlides[i].style.height = getInsideWindowHeight( ) -200 + "px";
    }
    slideInterval = setInterval("next( )", 5000);
}

15.4.4 See Also

Recipe 13.1 for how to make an element positionable on the page; Recipe 13.3 for details on the DHTML API library; Recipe 12.9 for an introduction to transition effects in IE for Windows.

    Previous Section Next Section