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.
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.
|