Table of Contents
The symbol returns the reader to the top of the Table of Contents.
1. Introduction
I recently decided to revise an upcoming-event carousel for an organization. The carousel appears in two places:
- As a series of slides displayed on a TV monitor, in the organization's lounge, by a Raspberry Pi 4. Each slide is shown for 10 seconds. When all the slides have been displayed, the slideshow repeats.
- As a carousel on the organizations website. The website carousel is a duplicate of the lounge slideshow with controls that allow pausing the slideshow, changing slide display time, and reversing the direction of the display.
The decision to implement the website carousel was driven by the fact that some of the slides have sizeable content that is not easily read in the 10 seconds allotted. Since the slides were already placed on the website for downloading to the Raspberry Pi 4, the decision to build a carousel to display the slides on the website was an easily accomplished task - or so I thought.
There appear to be as many ways to implement a carousel as there are websites that utilize them. This article describes one more that may be useful to some readers. It also describes some design decisions and some backtracking that occurred during implementation.
2. Requirements
The requirements levied against the website carousel were:
- Be capable of displaying at least 30 slides (this implementation will display up to 32 slides).
- Obtain the slides from a directory located on the website.
- Allow the user to control the direction of slide movement (i.e., to the right or to the left).
- Allow the user to pause and restart the display movement.
- Allow the user to display a specific slide.
- Allow the user to modify the time for which each slide will be displayed.
3. Design
The original design of the carousel used the <input type="range"...> HTML element. However, a couple of problems arose:
- The attempt to align tick marks with the position of the range slider was, at best, "close" but not "exact." There was no way to predict where the range slider stopped as it moved from one step to the next.
- Browser implementers differ significantly in how they interpret the W3c draft for the <input type="range"...> [^] HTML element. This means that a large amount of CSS [^] was required to make the element look consistent across browsers.
These problems resulted in a second design in which the <input type="range"...> HTML element was replaced by a set of selectable tick marks, depicted in the following design diagram.
Excluding the carousel_container <div>, there are three major <div>s.
- The left_slide_right <div> displays a left movement control, a slide, and a right movement control. Each component is contained in its own <div>. The arrows point in the direction in which the slide movement will occur when clicked.
- The pause_continue_dots <div> contains the pause/continue control and the small circles representing the number of slides that will be shown (in this case 20 slides). The pause/continue control and the unfilled/filled-circles components are each contained in its own <div>. The filled circle indicates which slide is currently being displayed. By clicking on an unfilled dot, the slide that the dot represents is displayed. In this figure, the slide movement is paused and the continue arrow appears to the left of the dots. When the continue arrow is clicked, movement starts again and the continue arrow is replaced by the pause indicator (seen in this figure to left of the carousel_container <div>).
- The ticks_and_delay <div> contains the means by which a user can change the amount of time allotted to the display of slides. It contains selectable tick marks that allow the user to click on a tick mark to set the desired delay.
When implemented, the carousel appears as:
4. Implementation
The implementation described in this article uses HTML, CSS, vanilla JavaScript, and SVG. No third-party software was used.
4.1. HTML
left_slide_right <div>, pause_continue_dots <div>, and ticks_and_delay <div> define flex boxes. I can assure my readers that unadorned <div>s (without using flex) or <table>s were not the solution (they were tried!).
4.2. CSS
The flex boxes are defined in CSS.
.carousel_container,
.left_slide_right_container {
display:flex;
justify-content:center;
align-items:flex-start;
}
.left_slide_right {
height:300px;
display:flex;
justify-content:center;
align-items:center;
}
.pause_continue_dots_container,
.time_ticks_div,
.ticks {
display:flex;
justify-content:center;
align-items:center;
}
#pause_continue_button,
#dots {
display:flex;
justify-content:center;
}
The dots, both filled and unfilled, were also defined in CSS.
.dot {
width:10px;
height:10px;
margin-right:2px;
border:solid 1px Black;
border-radius:50%;
}
.dot:hover {
border-color:Red;
}
.unfilled {
background:White;
border-color:Black;
}
.filled {
background:CornflowerBlue;
border-color:CornflowerBlue;
}
The preceding CSS appear as internal CSS in the style element of the carousel.html document. The CSS provided in the internal CSS is specific to this Carousel implementation.
Although, parts of the CSS for the carousel appear within the internal CSS, inline CSS was also used. This form of CSS usually set values for width and height since this styling was only applicable to specific HTML elements.
Additional CSS was generated in the JavaScript found in carousel.js.
Of interest may be that a dot is only defined through CSS. Whether a specific dot is filled or unfilled is determined by the JavaScript function set_active_dot.
The dots are separated from their left neighbors by 2 pixels; the control is separated from the dots <div> by 5 pixels. These values were chosen by experimenting to find an acceptable balance.
4.3. Javascript
By far, the most complex piece of code is the project's Javascript module, contained in carousel.js, and whose "namespace" is "Carousel".
Execution of the JavaScript is controlled by a JSON object.
<script>
var CAROUSEL_COMPONENTS =
{
"slides_directory":"./Slides30",
"timer_default_value":2,
"timer_minimum":1,
"timer_maximum":20,
"timer_step":1,
"chosen":"Red",
"hover":"DodgerBlue",
"normal":"LightSkyBlue",
"debug_json":false
};
</script>
This JSON object should be defined in the <head> of carousel.html.
The slides that are to be displayed are expected to be in the single directory named in CAROUSEL_COMPONENTS.slides_directory. The slides themselves are named using the form
Slide1.png,Slide2.png, ....
The list of slides in the directory is recovered using XMLHttpRequest. The list returned includes more information than just the names of the slides. Each line in the XMLHttp.responseText ends with '\n' so a file list can be created by spliting the XMLHttp.responseText by that character. To extract an actual list of slides, the execution environment must be considered. This is accomplished with the following.
var LOCAL_DOMAINS = [ "localhost", "127.0.0.1" ];
var IS_LOCALHOST =
LOCAL_DOMAINS.includes ( window.location.hostname );
The JavaScript function extract_slides_from_file_list is responsible for the generation of a list of slides. This is the function that requires change in the event that the slides are named differently than described above.
There is a limit to the number of slides that can be displayed in this implementation (limited by the number of filled/empty dots that can fit below the slide image).
var MAXIMUM_SLIDES_ALLOWED = 32;
There is also a limit to the number of tick marks that can be displayed in this implementation (limited by the number of tick marks and the legends that can fit within the carousel_container).
var MAXIMUM_TICK_MARKS_ALLOWED = 35;
Because the interaction between JavaScript and HTML is significant, a large number of document ids were required to be instantiated.
var carousel = document.getElementById ( "carousel" );
var dots = document.getElementById ( "dots" );
var left_side_BUT = document.getElementById ( "left_side_BUT" );
var pause_continue_button = document.getElementById (
"pause_continue_button" );
var right_side_BUT = document.getElementById ( "right_side_BUT" );
var right_text = document.getElementById ( "right_text" );
var temporary_dot = document.getElementById ( "temporary_dot" );
var ticks = document.getElementById ( "ticks" );
Javascript supplies the event handlers for all of the carousel events including:
dot_clicked
move_left
move_right
pause_continue_clicked
tick_clicked
tick_mouseout
tick_mouseover
There are five state variables:
var current_active_dot = 0;
var current_seconds = 0;
var current_tick_mark_border_color;
var moving_leftward = true;
var paused = false;
These variables either save/restore critical execution state or control how the GUI displays its content.
4.4. Scalable Vector Graphics (SVG)
SVG [^] was used to generate the images (other than dots) that appear in the GUI.
For example, the pause and continue images are generated using SVG in the JavaScript function assign_pause_or_continue_image.
function assign_pause_or_continue_image ( )
{
pause_continue.innerHTML = "";
if ( paused )
{
pause_continue.innerHTML =
"<svg class='continue_icon'" +
" width='10' " +
" height='10'" +
" viewBox='0 0 10 10'" +
" overflow='visible'>" +
" <path d='M 0 0 L 10 5 L 0 10 L 0 0' " +
" fill='" + COMPONENTS.normal + "'" +
" stroke='" + COMPONENTS.normal + "'/>" +
"</svg>";
}
else
{
pause_continue.innerHTML =
"<svg class='pause_icon' " +
" width='10'" +
" height='10'" +
" viewBox='0 0 10 10'>" +
" <path d='M 0 0 h 4 v 10 h -4 v -10 m 6 0 h 4 v 10 h -4 v -10'" +
" fill='" + COMPONENTS.normal + "'" +
" stroke='" + COMPONENTS.normal + "'/>" +
"</svg>";
}
}
This code uses the state variable paused to determine which figure to draw. The figure outlines are created using a path. In the continue_icon, "M" moves a pen to an absolute location and "L" draws a line from the current location to an absolute location. The path is then closed. In the pause_icon, "m" moves the pen from its current position to a relative location, "h" draws a horizontal line and "v" draws a vertical line for the specified length. Because they are dynamic, these two figures are drawn with JavaScript. The left- and right-movement arrows are drawn using SVG embedded in the HTML.
<!--
<div class="left_slide_right"
style="width:25px;" >
<button id="left_side_BUT"
style="background:transparent;">
<svg version="1.1"
width="20"
height="23"
xmlns="http://www.w3.org/2000/svg">
<polygon id="left_pointing_arrow"
points="0,11 20,0 20,23"
fill="CornflowerBlue"
stroke="CornflowerBlue"/>
</svg>
</button>
</div>
⋮
<!--
<div class="left_slide_right"
style="width:25px;
margin-left:1px;" >
<button id="right_side_BUT"
style="background:transparent;
float:left;
vertical-align:middle;">
<svg version="1.1"
width="20"
height="23"
xmlns="http://www.w3.org/2000/svg">
<polygon id="right_pointing_arrow"
points="0,0 20,11 0,23"
fill="CornflowerBlue"
stroke="CornflowerBlue"/>
</svg>
</button>
</div>
The figure outlines are created using a polygon which is defined as a series of points. The SVG polygon closes itself.
5. Download
The carousel.zip file contains the following files in the following directories (directories are bold)
Carousel_Project
carousel.html Project HTML
carousel.js Project JavaScript
favicon.ico Project Icon
Slides30 Slide directory derived from Slides30.ppt
Slide1.png
⋮
Slide30.png
Carousel_Sources
BW_Slides30.ppt
Initial_Design.ppt
Miscellaneous.ppt
Slides20.ppt
Slides30.ppt
Slides40.ppt
Carousel_Paper
Carousel.html This article
carousel.png
Carousel.TOC.html This article with a TOC
Chrome.png
Edge.png
Firefox.png
Firefox_Developer.png
four_svgs.PNG
Internet_Explorer.png
Opera.png
overview.png
pause_continue_dots.png
range_vs_div.png
ReturnToTOC.png
Safari.png
For readers who intend to download the ZIP file, I suggest the following:
- Create the directory Carousel.
- Download the ZIP file into Carousel.
- Extract the contents of the ZIP file into the directory Carousel. Note some ZIP file extractors want to extract the contents into a newly created directory under Carousel.
- Using your favorite browser, open Carousel_Project/carousel.html.
Please advise me of any suggestion, comments, or criticisms. They are all equally welcome.
6. References
7. Conclusion
This article has described the implementation of a lightweight, dynamic carousel that requires no third-party software.
8. Developement Environtment
The Carousel Project was developed in the following environment:
Microsoft Windows 7 Professional SP 1 |
Microsoft Visual Studio 2008 Professional SP1 |
Microsoft Visual C# 2008 |
Microsoft .Net Framework Version 3.5 SP1 |
9. Supported Browsers
The following depicts the browsers that support this implementation of a carousel.
| | | | | | |
Chrome
103 | Edge
91.0.625 | Firefox
95.0.2 | Firefox
Developer
103.0b3 | Internet
Explorer 11 | Opera
88.0.4412.40 | Safari
5.1.7 |
Neither Internet Explorer nor Safari have revisions for Windows 7.
10. History
07/04/2022 | Original article |