How to create an image slide show on a webpage
How to create an image slide show on a webpage
slideShow.js
) that produces a slide show in which images fade in an out as specified by a given list of <img>
elements.
Note Because addEventListener is used, this content is only applicable to Windows Internet Explorer 9 and later. Prior to Internet Explorer 9, attachEvent must be used. For more info, see How to Create Effective Fallback Strategies.
- How to use slideShow.js to create a slide show from a list of img tags
- Implementation details of slideShow.js
Using slideShow.js
This example shows the simplest markup needed to produce such a slide show:<!DOCTYPE html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <!-- For intranet testing only, remove in production. --> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <title>Slide Show</title> </head> <body> <div id="slideShowImages"> <img src="images/slide1.jpg" alt="Slide 1" /> <img src="images/slide2.jpg" alt="Slide 2" /> <img src="images/slide3.jpg" alt="Slide 3" /> <img src="images/slide4.jpg" alt="Slide 4" /> </div> <button id="slideShowButton"></button> <!-- Optional button element. --> <script src="slideShow.js"></script> </body> </html>
slideShow.js
to create a slide show, the follow markup must be present:- A
<div>
wrapper, whose ID is exactlyslideShowImages
, containing<img>
elements that point to the desired slide show images. - A script element pointing to
slideShow.js
.
Note
slideShowButton
.
If you don't use this toggle button, the user can stop or start the
slide show by clicking any one of the cycling slide show images.
Additionally, if you use slideShow.js
, you can easily style the slide show <div>
wrapper along with its child images as shown in this code sample:<!DOCTYPE html> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <!-- For intranet testing only, remove in production. --> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <title>Slide Show</title> <style> #slideShowImages { /* The following CSS rules are optional. */ border: 1px gray solid; background-color: lightgray; } #slideShowImages img { /* The following CSS rules are optional. */ border: 0.8em black solid; padding: 3px; } </style> </head> <body> <div id="slideShowImages"> <img src="images/slide1.jpg" alt="Slide 1" /> <img src="images/slide2.jpg" alt="Slide 2" /> <img src="images/slide3.jpg" alt="Slide 3" /> <img src="images/slide4.jpg" alt="Slide 4" /> </div> <button id="slideShowButton"></button> <!-- Optional button element. --> <script src="slideShow.js"></script> </body> </html>
slideShow.js
is that, slide images of varying dimensions are automatically centered within the <div>
wrapper, and the transitions between the differently sized images look
reasonable in that as one slide is faded out, the other is concurrently
faded in.Now you can use
slideShow.js
to create a slide show from a list of <img>
elements. The remainder of this topic is dedicated to explaining the implementation details of slideShow.js
.
Note To change the speed of the slide show or the text of the toggle button, see the Globals section.
Implementation details
As can be seen by reviewingslideShow.js
, this JavaScript file is composed of essentially two items, the line window.addEventListener('load', slideShow, false)
and its associated callback function slideShow
.Because the function
slideShow
references <div id="slideShowImages">
(and possibly <button id="slideShowButton">
), we must be sure that these elements are available in the DOM before doing so. This is accomplished via window.addEventListener('load', slideShow, false)
. In other words, slideShow
is called only when the webpage's markup is available from the DOM. Another advantage of placing the majority of slideShow.js
's code within a single function is that it protects the namespace of slideShow.js
users.Now that we understand when
slideShow
is invoked, let's turn to the details of slideShow
itself, starting with its core algorithm:Core algorithm
At the heart ofslideShow
's algorithm is the CSS layout of <div id="slideShowImages">
and its children <img>
elements. This CSS layout is defined within slideShow.js
's initializedSlideShowMarkup
function. In particular, initializedSlideShowMarkup
:- Sets the CSS
position
of<div id="slideShowImages">
torelative
. This allows any of its children, which are positioned absolutely, to be positioned relative to this container. If<div id="slideShowImages">
were set tostatic
positioning (the default), its children that are absolutely positioned would instead be positioned relative to the<body>
element and not<div id="slideShowImages">
. - Positions all slide show
<img>
elements absolutely. This stacks (or piles) all such<img>
elements on top of each within their<div id="slideShowImages">
container. Think of the slides as a vertical stack of images with the first slide (the first listed<img>
element) on the bottom of the stack and the last slide (the last listed<img>
element) on the top of the stack. - Changes the opacity of each slide (in the stack) to 0 to make all slides completely transparent. Then, the first
<img>
element’s opacity is set to 1. This makes the slide on the bottom of the stack visible through all the transparent slides on top of it.
- Let the current slide (in the stack) be the bottom-most slide (the first
<img>
element). - Move to the next slide in the stack. If there is no next slide, let the next slide be the first slide.
- Set the opacity value of the current slide to 1 (visible) and the opacity value of the next slide to 0 (transparent).
- Concurrently and "slowly" change the opacity value of the current slide from 1 to 0 and the opacity value of the next slide from 0 to 1. This "slowly" fades out the current slide while fading in the next slide.
- Let the current slide be the next slide.
- Go to step 2 until the user signals that the slide show should be stopped.
slideShow
function, which includes these components:- Globals
- Main
- initializeGlobals
- insufficientSlideShowMarkup
- initializeSlideShowMarkup
- maxSlideWidth and maxSlideHeight
- startSlideShow
- haltSlideShow
- toggleSlideShow
- transitionSlides and fadeActiveSlides
Globals
The first item inslideShow
is an object literal called globals
. This object is used to contain all of slideShow
's "global variables" in one obvious (and handy) place. For example, to access slideDelay
from any place within slideShow
, just invoke globals.slideDelay
.The first six of these "global variables" are of particular interest:
slideDelay: 4000, fadeDelay: 35, wrapperID: "slideShowImages", buttonID: "slideShowButton", buttonStartText: "Start Slides", buttonStopText: "Stop Slides",
slideDelay
, determines the amount of
time, in milliseconds, that any given slide is visible on the screen. In
this case, each slide will be visible for a total of 4 seconds.To describe
fadeDelay
, we refer back to the core algorithm used in slideShow.js
. Consider our stack of slide images. The slide on the bottom of the stack has an opacity
value of 1 (is visible) while all others have an opacity value of 0
(transparent). To perform a fade operation between the first slide and
the next slide, we decrease the opacity of the first slide from 1 to 0,
while at the same time, increasing the opacity of the second from 0 to
1. Precisely half way through this operation, both slides will have an
opacity value of 0.5. At the end of this operation, the first slide has
an opacity value of 0 (transparent) and the second an opacity value of 1
(visible). With this understanding in place, fadeDelay
represents the amount of time between individual opacity changes. For example, if fadeDelay
is 35, there will be 35 milliseconds between each opacity change. The
opacity change amount is the reciprocal of this value, which in this
case, is about 0.0286. Because the opacity increment value is 1/35, it
will take 35 such values to increment 0 to 1 (and similarly, 35 such
values to decrement 1 to 0). Thus, the total amount of time it takes to
complete one fade operation is 35 milliseconds times 35 or 35² = 1225
milliseconds = 1.225 seconds. Likewise, if fadeDelay
were
45, it would take 45² = 2.025 seconds to complete a single fade
operation between two consecutive slides. Bottom line, adjust slideDelay
and fadeDelay
to your liking but always keep fadeDelay
significantly smaller than slideDelay
.Moving on,
wrapperID
and buttonID
must match the ID values used in the markup: <div id="slideShowImages">
and <button id="slideShowButton">
.buttonStartText
and buttonStopText
define
the text to be used in the (optional) slide show toggle button. You can
change these labels (strings) to whatever fits the need of your app.For information about the remaining "global variables,"see the comments in the
slideShow.js
file.Main
Now we'll describe the "main" code block of theslideShow
function:initializeGlobals(); if ( insufficientSlideShowMarkup() ) { return; } // Assert: there's at least one slide image. if (globals.slideImages.length == 1) { return; } // Assert: there are at least two slide images. initializeSlideShowMarkup(); globals.wrapperObject.addEventListener('click', toggleSlideShow, false); if (globals.buttonObject) { globals.buttonObject.addEventListener('click', toggleSlideShow, false); } startSlideShow();
initializeGlobals
initializesglobals
, providing access to the<div>
wrapper element, its<img>
children, and the toggle button element (if present).insufficientSlideShowMarkup
returnsfalse
if the markup expected byslideShow.js
appears to be present,true
otherwise. As can be seen in the code example, iftrue
is returned, theslideShow
function is immediately exited, and silently terminatesslideShow.js
(as by design).- If
globals.slideImages.length == 1
istrue
, there's only one slide show<img>
element in markup and it's already onscreen; we simply terminateslideShow.js
(leaving this solo image onscreen). initializeSlideShowMarkup
prepares the given slide show markup (as described in the core algorithm section) for the forthcoming slide show.- We next add an event listener,
toggleSlideShow
, such that when the<div>
wrapper element is clicked, the slide show is toggled off (if on) and on (if off). - Next, if
globals.buttonObject
istrue
, the slide show toggle button is present in markup and we attach the same event listener function (toggleSlideShow
) to it. - Finally, we call
startSlideShow
to start the slide show in earnest.
slideShow
, are described, in detail, next.initializeGlobals
This is the code for the nested functioninitializeGlobals
:function initializeGlobals() { globals.wrapperObject = (document.getElementById(globals.wrapperID) ? document.getElementById(globals.wrapperID) : null); globals.buttonObject = (document.getElementById(globals.buttonID) ? document.getElementById(globals.buttonID) : null); if (globals.wrapperObject) { globals.slideImages = (globals.wrapperObject.querySelectorAll('img') ? globals.wrapperObject.querySelectorAll('img') : []); } }
globals
(that is, gloabals.wrapperID
and globals.butonID
), we obtain a reference to them if they're present in markup. Then, using querySelectorAll
, we fill an array with slide <img>
objects. This array, globals.slideImages
, works as our metaphorical stack as described in the core algorithm section.insufficientSlideShowMarkup
This is the code for the nested functioninsufficientSlideShowMarkup
:function insufficientSlideShowMarkup() { if (!globals.wrapperObject) { if (globals.buttonObject) { globals.buttonObject.style.display = "none"; } return true; } if (!globals.slideImages.length) { if (globals.wrapperObject) { globals.wrapperObject.style.display = "none"; } if (globals.buttonObject) { globals.buttonObject.style.display = "none"; } return true; } return false; }
false
if all the expected markup is present, and true
otherwise. As you can see in this code, if it's determined that not all
the expected markup is present, the code tries to clean up the
associated slide show markup (that is, sets the value of the CSS display
property to "none"
,) before returning true
.initializeSlideShowMarkup
This is the code for the nested functioninitializeSlideShowMarkup
:function initializeSlideShowMarkup() { var slideWidthMax = maxSlideWidth(); var slideHeightMax = maxSlideHeight(); globals.wrapperObject.style.position = "relative"; globals.wrapperObject.style.overflow = "hidden"; globals.wrapperObject.style.width = slideWidthMax + "px"; globals.wrapperObject.style.height = slideHeightMax + "px"; var slideCount = globals.slideImages.length; for (var i = 0; i < slideCount; i++) { globals.slideImages[i].style.opacity = 0; globals.slideImages[i].style.position = "absolute"; globals.slideImages[i].style.top = (slideHeightMax - globals.slideImages[i].getBoundingClientRect().height) / 2 + "px"; globals.slideImages[i].style.left = (slideWidthMax - globals.slideImages[i].getBoundingClientRect().width) / 2 + "px"; } globals.slideImages[0].style.opacity = 1; if (globals.buttonObject) { globals.buttonObject.textContent = globals.buttonStopText; } }
- Sets the CSS
position
values of<div id="slideShowImages">
and its<img>
children, as described in core algorithm section. - Adjusts the
width
andheight
of the<div id="slideShowImages">
container so that the slide image(s) with the widest width and tallest height will fit exactly within it (in case the slide images have differing dimensions). - Adjusts the
absolute
positioning of the slide images so that they're centered within the<div id="slideShowImages">
container (again, in case the slide images have inhomogeneous dimensions). This is done by pushing any given smaller image away from the top-left corner of its container by ½ of the margin below and to the right of it:
- Lastly, if the toggle button is present in markup, the function sets the toggle button's "stop the slide show" text (in that, by default, the slide show is initially running).
maxSlideWidth and maxSlideHeight
These two functions return the width of the widest slide image and the height of the tallest slide image (which might be two different images). Because both functions are nearly identical, we'll only look atmaxSlideWidth
:function maxSlideWidth() { var maxWidth = 0; var maxSlideIndex = 0; var slideCount = globals.slideImages.length; for (var i = 0; i < slideCount; i++) { if (globals.slideImages[i].width > maxWidth) { maxWidth = globals.slideImages[i].width; maxSlideIndex = i; } } return globals.slideImages[maxSlideIndex].getBoundingClientRect().width; }
getBoundingClientRect
method always returns a value in
pixels and includes all specified CSS properties such as borders,
margins, and so forth. This ensures that <div id="slideShowImages">
is wide and tall enough to accommodate all such CSS additions applied to the slide images.startSlideShow
The code for the nested functionstartSlideShow
is straightforward:function startSlideShow() {
globals.slideShowID = setInterval(transitionSlides, globals.slideDelay);
}
transitionSlides
be called every gloabls.slideDelay
milliseconds until such repetitive invocations are halted by calling clearInterval(globals.slideShowID)
.haltSlideShow
As you may have anticipated, the code forhaltSlideShow
is composed of a single line:function haltSlideShow() {
clearInterval(globals.slideShowID);
}
globals.slideDelay
milliseconds, of transitionSlides
, halting the slide show.toggleSlideShow
This is the code for the nested functiontoggleSlideShow
:function toggleSlideShow() { if (globals.slideShowRunning) { haltSlideShow(); if (globals.buttonObject) { globals.buttonObject.textContent = globals.buttonStartText; } } else { startSlideShow(); if (globals.buttonObject) { globals.buttonObject.textContent = globals.buttonStopText; } } globals.slideShowRunning = !(globals.slideShowRunning); }
addEventListener
calls made in the main code block:globals.wrapperObject.addEventListener('click', toggleSlideShow, false); if (globals.buttonObject) { globals.buttonObject.addEventListener('click', toggleSlideShow, false); }
toggleSlideShow
is invoked whenever <div id="slideShowImages">
or <button id="slideShowButton">
(if present) are clicked.Now because
globals.slideShowRunning
is initially set to true
, on the first invocation of toggleSlideShow
, haltSlideShow
is called and (if present) the text for the toggle button is changed to its "start the slide show" form. Then, globals.slideShowRunning
is negated, which indicates the current state of the slide show (off, in this case).When
toggleSlideShow
is called again (when a slide show image or the toggle button is clicked), globals.slideShowRunning
is false
, so startSlideShow
is invoked, the button text is switched to its "shut off the slide show" form, and globals.sideShowRunning
is negated (indicating that the slide show is now on/running). Stated more succinctly,
toggleSlideShow
does just that - it turns the slide show on and off by calling startSlideShow
and haltSlideShow
, as determined by the current state of globals.slideShowRunning
.transitionSlides and fadeActiveSlides
The core algorithm is primarily implemented in thetransitionSlides
function:function transitionSlides() { var currentSlide = globals.slideImages[globals.slideIndex]; ++(globals.slideIndex); if (globals.slideIndex >= globals.slideImages.length) { globals.slideIndex = 0; } var nextSlide = globals.slideImages[globals.slideIndex]; var currentSlideOpacity = 1; var nextSlideOpacity = 0; var opacityLevelIncrement = 1 / globals.fadeDelay; var fadeActiveSlidesID = setInterval(fadeActiveSlides, globals.fadeDelay); function fadeActiveSlides() { currentSlideOpacity -= opacityLevelIncrement; nextSlideOpacity += opacityLevelIncrement; // console.log(currentSlideOpacity + nextSlideOpacity); if (currentSlideOpacity >= 0 && nextSlideOpacity <= 1) { currentSlide.style.opacity = currentSlideOpacity; nextSlide.style.opacity = nextSlideOpacity; } else { currentSlide.style.opacity = 0; nextSlide.style.opacity = 1; clearInterval(fadeActiveSlidesID); } } }
globals.slideImages
is an array of slide image objects and that globals.slideIndex
is initially set to 0. With this in mind, here's the pseudocode for transitionSlides
:- Let the current slide be the one indicated by
globals.slideIndex
:var currentSlide = globals.slideImages[globals.slideIndex];
- Move to the next slide and if there is no next slide, start from the beginning:
++(globals.slideIndex); if (globals.slideIndex >= globals.slideImages.length) { globals.slideIndex = 0; }
- Let the next slide be the one indicated by
globals.slideIndex
:var nextSlide = globals.slideImages[globals.slideIndex];
- Prepare the local variables used to fade out the current slide and fade in the next slide:
var currentSlideOpacity = 1; var nextSlideOpacity = 0; var opacityLevelIncrement = 1 / globals.fadeDelay;
- Call the local function
fadeActiveSlides
everyglobals.fadeDelay
milliseconds until both slides have completed faded. That is,fadeActiveSlides
is invoked everyglobals.fadeDelay
milliseconds whilecurrentSlideOpacity >= 0
andnextSlideOpacity <= 1
. If one of these becomesfalse
, both slides are, for all practical purposes, fully faded, and so we explicitly set their opacity values and terminate the repetitive invocations offadeActiveSlides
in theelse
clause:currentSlide.style.opacity = 0; nextSlide.style.opacity = 1; clearInterval(fadeActiveSlidesID);
fadeActiveSlides
shows, the current
slide's opacity is decreased by the same amount that the next slide's
opacity is increased, and because opacity values are always between 0
and 1, it follows that the sum of these two opacity values must always
be 1 (or at least very close to 1, given rounding errors). This, in
fact, is true and can be verified by calling console.log(currentSlideOpacity + nextSlideOpacity)
as suggested by the comment in the previous code example, and repeated here:// console.log(currentSlideOpacity + nextSlideOpacity);
console.log
, see How to use F12 Developer Tools to Debug your Webpages.CSS role in implementation of slideShow.js
Paradoxically, it's the CSS that allows for the implementation of this JavaScript slide show library (slideShow.js
). In particular, setting <div id="slideShowImages">
's CSS position
value to relative
and all its <img>
children to a position
value of absolute
allows for a "stack" of transparent slide images. It then becomes a
matter of tweaking the opacity values of adjacent slides to create a
fading-style slide show.Finally, there are a many different ways to implement a JavaScript based slide show library, including various jQuery plugins. By providing a detailed explanation of at least one such implementation, it becomes easier to either write your own customized slide show library or modify
slideShow.js
to suit your specific needs (when required).
No comments
Post a Comment