How to Create a Clickable Arrow or Large dot Image Slideshow Carousel using CSS and JavaScript
Image Gallery
How To, for Front End and Web Applications
By: Chrysanthus Date Published: 26 Apr 2025
A Slideshow or Carousel is a set of webpage images, where the next one is viewed by clicking a button This tutorial explains How to Create a Clickable Arrow or Large dot Image Slideshow Carousel using CSS and JavaScript. For this project, there are four images. The images are numbered from 1 to 4. At the top of the webpage, there is a big wide rectangular pane, where each image appears. At the left middle end of the pane is a left arrow-head button. If this button is clicked, the previous image will appear. If the current image is the first image, then the last image will appear. At the right middle end of the pane, is a right arrow-head button. If this button is clicked, the next image will appear. If the current image is the last image, the first image will appear.
Below the big wide rectangular pane, are four circular large dots in a short horizontal line. Each dot corresponds to an image. If the first dot is clicked, the first image will be displayed in the pane. If the second dot is clicked, the second image will be displayed in the pane. If the third dot is clicked, the third image will appear in the pane; and so on.
There is a bit of responsiveness, in the sense that, when the screen width is less than or equal to 500px, the font-size of the arrows and the description paragraph, becomes 11px instead of their higher values.
There is also some animation; see details below.
The complete webpage code is given at the end of this tutorial. The reader should copy the complete code into a text editor. Save the file with any name, but with the extension, .html . Open the file in the browser. Click the right arrow-head several times to see the new images. Click the left arrow-head several times to see the new images. Click some of the dots to see the new images. The images are in the broad-network.com website. So the reader has to be hooked on to the Internet. The reader is advised to do that before carrying on to read the rest of this tutorial.
The Webpage Skeleton Code
The skeleton code for the webpage to which more useful code will be added is:
<!DOCTYPE html> <html> <head> <title>Image Carousel</title> <link rel='icon' href='https://www.examplee.com/images/logo.jpg' type='image/x-icon'> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content="Description of the Web Page."> <style> </style> <script type="text/javascript"> </script> </head> <body> </body> </html>
Any modern professional webpage should have at least the first four tags in the HTML head element above. The HTML style element will have quit some good amount of code. The JavaScript code will respond to the click of an arrow-head button or a large circular dot, in order to show its image in the pane. The HTML body element will have the HTML divs for the images.
HTML body Element Content
The content of the HTML body element is:
<div class="slideshow-container"> <div class="mySlides fade" style='display:block'> <p class="numbertext">1 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_nature_wide.jpg" style="width:100%"> <p class="text">Nature and sunrise</p> </div> <div class="mySlides fade"> <p class="numbertext">2 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_snow_wide.jpg" style="width:100%"> <p class="text">Snowy Mountains</p> </div> <div class="mySlides fade"> <p class="numbertext">3 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_mountains_wide.jpg" style="width:100%"> <p class="text">Mountains and fjords</p> </div> <div class="mySlides fade"> <p class="numbertext">4 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_lights_wide.jpg" style="width:100%"> <p class="text">Northern Lights</p> </div> <a class="prev" onclick="plusSlides(-1)">〈</a> <a class="next" onclick="plusSlides(1)">〉</a> </div> <br> <div style="text-align:center"> <span class="dot active" onclick="currentSlide(1)"></span> <span class="dot" onclick="currentSlide(2)"></span> <span class="dot" onclick="currentSlide(3)"></span> <span class="dot" onclick="currentSlide(4)"></span> </div>
There is an outer div element, with class-name, "slideshow-container" . This div has four inner divs. Each inner div has the class-name, "mySlides fade" . "mySlides" refers to a CSS rule within the start and end tags in the HTML style element. "fade" refers to another CSS rule within the start and end tags in the HTML style element.
Each inner div has a paragraph element, followed by the image tag, and then another paragraph element. There are four inner divs, and each has a different image (tag). The first paragraph per inner div, has the numbers for the different images, which are 1/4, 2/4, 3/4, and 4/4. The content of the paragraph after the image has a short description of the image.
The outer div is given a maximum width of 1000px. The assumption is, that the smaller screens will give smaller widths; a somewhat responsiveness. Each image is given a width of 100% in the HTML body. This is 100% of its inner div. The inner div is not given any width. It just fits into the outer div. So this is 100% of the outer div.
The outer div.slideshow-container should be considered as the pane. Changing the image means changing the whole inner div, which will come with its image, alongside the number-text in the first paragraph, and also the short image description in the second paragraph.
After the outer div in the code, are the previous (left) and next (right) arrow buttons. These are not HTML button elements. They are anchor (hyperlink) elements. The content arrow-head for the prev (previous) anchor element is "〈" (code for a less-than sign). The content arrow-head for the next (next) anchor element is "〉" (code for a greater-than sign). Each of these anchor elements has an onclick event, to call JavaScript to bring forth the previous or next inner div with its image and two paragraphs.
After the outer div in the code, is another div (below), without a class-name, but with the style attribute, "style='text-align:center, " . There are four span elements in this div. A span element becomes a large dot. The class-name of each span element is "dot" . They will be made round and large, in the HTML style element (within the start and end tags). Each span element has the onclick event, to display the corresponding inner div with its image and two paragraphs, in the pane div.
Content for the HTML Style Element
A lot of the styling is in the HTML style element (within the start and end tags).
At the top within the style element content, is the CSS rule:
* { box-sizing: border-box; }
This is called the box-sizing property. The asterisk, * in front of the rule, means that it is applied to all the containers, such as the div element. The box-sizing property means that the width of each element is measured from the left edge of the left border, to the right edge of the opposite border, instead of from the left edge of the element's content, to the right edge of the element's content. The box-sizing property also means that the height of each element is measured from the top edge of the top border, to the bottom edge of the opposite border, instead of from the top edge of the element's content, to the bottom edge of the element's content. The box-sizing property makes coding convenient for projects like this one.
Styling the body Element
The CSS rule for the body element is:
body { font-family: Verdana, sans-serif; margin:0 }
If the browser does not have the Verdana font, then it should use a sans-serif font. The gap for all the four margins is 0px. This means that the left edge of the main content will align with the left edge of the webpage; the top edge of the main content will align with the top edge of the webpage; and the right edge of the main content will align with the right edge of the webpage.
Styling the Outer div
The class-name of the outer div is "slideshow-container". It has the four inner divs for the images. The CSS rule for it is:
div.slideshow-container { max-width: 1000px; position: relative; margin: auto; }
The outer div is given a maximum width of 1000px. The assumption is, that the smaller screens will give smaller widths; a somewhat responsiveness. Each image is given a width of 100% in the HTML body. This is 100% of its inner div. The inner div is not given any width. It just fits into the outer div. So this is 100% of the outer div.
If the reader had tested the complete code, he/she would have noticed, that at the top left area of the large pane, is the number-text, 1/4, or 2/4, or 3/4, etc. for each of the four images. Such number-text is in a paragraph element and the paragraph element is in another layer, placed in front of the image. The paragraph element will have a CSS absolute position property; so its parent element, or grand parent element, the div.slideshow-container, needs to have a position property. In this case, it is the relative position property (for the grand parent).
"margin: auto;" means that the browser should decide on the value for the margin for div.slideshow-container.
Styling the Inner divs
One of the CSS rules for each of the inner divs is:
div.mySlides { display: none }
So, all the inner divs are not displayed initially. However, when the webpage loads (for the first time), the first div (with first image) is displayed. If the reader looks carefully at the content of the body element again, he/she will notice that the first inner div has the attribute, "style='display: block;' " . With that the first inner div (with its first image and two paragraphs) is displayed, when the webpage loads (for the first time).
Styling the Number Text
At the top left corner of the pane, is the number-text, 1/4, or 2/4, or 3/4, etc. for each of the four images. Each of these is in a paragraph element in each of the four (first) inner divs. The class-name for these four paragraphs is numbertext. The CSS rule for each of these paragraphs is:
p.numbertext { position: absolute; top: 0; color: #f2f2f2; font-size: 12px; padding: 8px 12px; }
Each of these paragraphs (one per inner div), has the absolute position property, as each will be in a layer in front of the pane div. Each will be at the top of the pane div (outer div with "top: 0;"). Since text in a paragraph element starts on the left, so each number-text will be on the left of the paragraph, putting the number-text at the top-left corner of the image (pane div). It is assumed at this point, that the reader knows the meanings of the rest of the CSS properties in the p.numbertext CSS rule.
Styling for the Short Description
At the bottom-center area of the image, is the short description or name for the image. In the HTML body, each inner div has a paragraph after the image tag, with class-name, 'text'. This paragraph has the short description or name for its image. The paragraph needs to be styled. And the CSS rule for the paragraph is:
p.text { position: absolute; bottom: 8px; padding: 8px 12px; width: 100%; color: #f2f2f2; font-size: 15px; text-align: center; }
This paragraph, has the absolute position property, and it is in a layer in front of the pane div. Its bottom will be 8px from the bottom of the pane div ("bottom: 8px;"). The text is horizontally centered in the paragraph. It is assumed at this point, that the reader knows the meanings of the rest of the CSS properties in the p.text CSS rule.
The outer div, the inner div and the image are in one layer. The two paragraphs (in one inner div) are in another layer in front of the image. The two paragraphs have the absolute position property in relation to their grand parent outer div, that has the relative position property.
Styling the prev and next Buttons
The prev and next Buttons are actually anchor elements. The CSS rule for each of them is:
a.prev, a.next { position: absolute; top: 50%; margin-top: -22px; width: auto; border-radius: 0 3px 3px 0; transition: 0.6s ease; cursor: pointer; padding: 16px; color: white; font-weight: bold; font-size: 22px; }
Note the comma in the selector, separating the expressions for the two buttons (arrow-heads). Like the two paragraphs in the inner div, the prev or next button has an absolute position of the layer in front of the image, in relation to the parent outer div. The inner div does not have any position property. So the paragraph elements and this arrow-head buttons, have to relate to the grand parent, which is the outer div.slideshow-container, which has a position property.
The top of each button is 50% down from the top of the outer div. With that, the center of the button is not at the vertical middle of the image (outer div). In order to raise the center up, the "margin-top: -22px;" property is used. "width: auto;" means that the browser chooses the width of the button.
The radius (curvature) of the top-left corner of the button is 0px. The radius (curvature) of the top-right corner of the button is 3px. The radius (curvature) of the bottom-rightt corner of the button is 3px. The radius (curvature) of the bottom-left corner of the button is 0px.
When the page loads, each of the buttons takes 0.6s to appear (transition: 0.6s ease;) .
With all that heavy styling, though the buttons are anchor elements, when the mouse pointer goes over the button (anchor element), it might no longer be a hand. In order to make sure it is a hand, the "cursor: pointer;" property is employed. It is assumed at this point, that the reader knows the meanings of the rest of the CSS properties in the "a.prev, a.next" CSS rule.
Additional Styling for the next Button
Do not forget that the prev and next button are actually anchor elements. The extra CSS rule for the next button is:
a.next { right: 0; border-radius: 3px 0 0 3px; }
When an element is produced in the normal way, it occurs at the left side of its parent container. In order to make the next button appear on the right of its parent element, the "right: 0;" property is used. The right end of the button is 0px to the left of the right end of the parent element (container).
The next button has a border-radius property that overrides that in the "a.prev, a.next" CSS rule above. With this overridden property, the radius (curvature) of the top-left corner of the button is 3px. The radius (curvature) of the top-right corner of the button is 0px. The radius (curvature) of the bottom-right corner of the button is 0px. The radius (curvature) of the bottom-left corner of the button is 3px.
Arrow-Head Buttons On-hover
The CSS rule for that is:
a.prev:hover, a.next:hover { background-color: rgba(0,0,0,0.8); }
When the mouse pointer goes over the button, the background color becomes black with no transparency.
Styling the Four Large Round Dots
The four large round dots are actually span elements. Span elements by default, in the body element, are not round elements. The CSS rule for each of the span elements is:
span.dot { display: inline-block; height: 15px; width: 15px; border-radius: 50%; cursor: pointer; margin: 0 2px; background-color: #bbb; }
Each span element is made a rectangular element with the CSS property, "display: inline-block;". The height of the rectangle is 15px, and the width of the rectangle is also 15px. That makes the rectangle a square. In order to convert the squares into circles, the four border-radii (curvature) for each of the squares, is made 50%. When the mouse pointer goes over a button, it becomes a hand, because of the property, "cursor: pointer;" . It is assumed at this point, that the reader knows the meanings of the rest of the CSS properties in the span.dot CSS rule.
Active CSS Rule and Span On-hover
The CSS rule to indicate the active large round dot, or the dot on hover is:
span.active, span.dot:hover { background-color: #717171; }
So, when the mouse pointer goes over a round large dot, its background color becomes #717171 (gray). The dot whose image is currently displayed, is said to be the active dot. JavaScript is used to indicate the dot that is active by giving it the color, #717171 .
Some Responsiveness
When the screen width is equal to or less than 500px, the font-size text of a.prev, a.next, and p.text become 11px, from their higher values. The media screen query for that is:
@media only screen and (max-width: 500px) { a.prev, a.next, p.text {font-size: 11px} }
Animation
Each image is in an inner div. If the reader tested the complete webpage code, he/she would have noticed that the image in the pane, takes 1.5s to have from an opacity of 0.4 (quit dull) to an opacity of 1 (bright). It is actually the inner div and its inner contents (including the image), that undergo that transformation. It is not the image alone. So, it is the inner div that has to be addressed. There are two animation CSS rules for this.
Notice in the HTML body element, that each inner div has the class-name, "mySlides fade". "mySlides" refers to the div.mySlides CSS rule mentioned above, while "fade" refers to two animation CSS rules (see below). The two CSS rules are:
div.fade { animation-name: fade; animation-duration: 1.5s; } @keyframes fade { from {opacity: 0.4} to {opacity: 1} }
The starting CSS rule for the animation has the selector, div.fade . This rule gives the name of the animation (which is fade) and the duration. The second and last CSS rule for the animation is a Keyframes CSS rule. This gives the lower and upper limits of the opacity.
JavaScript
When the left arrow-head button is clicked, the previous inner div has to be displayed in the pane div. And the rest of the inner divs have to be hidden, by giving the display property value of none. This show and hide, has to be done to the span elements (large round dots) as well (highlighting and de-highlighting).
When the right arrow-head button is clicked, the next inner div has to be displayed in the pane div. And the rest of the inner divs have to be hidden, by giving the display property value of none. This show and hide, has to be done to the span elements (large round dots) as well (highlighting and de-highlighting).
When a large round dot (span element) is clicked, its corresponding inner div has to be displayed in the pane div. And the rest of the inner divs have to be hidden, by giving the display property value of none. This show and hide, has to be done to the span elements (large round dots) as well (highlighting and de-highlighting).
The JavaScript code is:
<script type="text/javascript"> let slideIndex = 1; function plusSlides(n) { slideIndex += n; showSlides(slideIndex); } function currentSlide(n) { slideIndex = n; showSlides(n); } function showSlides(n) { let slides = document.getElementsByClassName("mySlides"); let dots = document.getElementsByClassName("dot"); if (n > slides.length) {slideIndex = 1} if (n < 1) {slideIndex = slides.length} for (let i = 0; i < slides.length; i++) { slides[i].style.display = "none"; } for (let i = 0; i < dots.length; i++) { dots[i].className = dots[i].className.replace(' active', ""); } slides[slideIndex-1].style.display = "block"; dots[slideIndex-1].className += " active"; } </script>
There are three functions in the code. The first function, plusSlides(n), is for the left and right arrow-head buttons. If the left arrow-head button is clicked, n is -1. If the right arrow-head button is clicked, n is 1 (+1). This function calls the third function, showSlides(n) .
The second function, currentSlide(n) is for the large round dots (span elements). When a span element is clicked, it sends an integer, between 1 and 4 (inclusive), as argument to this function, for the corresponding inner div to be shown. This function also calls the third function, showSlides(n) .
The third function, showSlides(n) , first hides all the inner divs and all the span elements. Then it displays the correct inner div and correct span element.
The Complete Webpage Code
And there you have it: the complete webpage code is:
<!DOCTYPE html> <html> <head> <title>Image Carousel</title> <link rel='icon' href='https://www.examplee.com/images/logo.jpg' type='image/x-icon'> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content="Description of the Web Page."> <style> * { box-sizing: border-box } body { font-family: Verdana, sans-serif; margin:0 } /* Slideshow container */ div.slideshow-container { max-width: 1000px; position: relative; margin: auto; } div.mySlides { display: none } /* Number text (1/4 etc) */ p.numbertext { position: absolute; top: 0; color: #f2f2f2; font-size: 12px; padding: 8px 12px; } /* Caption text */ p.text { position: absolute; bottom: 8px; padding: 8px 12px; width: 100%; color: #f2f2f2; font-size: 15px; text-align: center; } /* Next & previous buttons */ a.prev, a.next { position: absolute; top: 50%; margin-top: -22px; width: auto; border-radius: 0 3px 3px 0; transition: 0.6s ease; cursor: pointer; padding: 16px; color: white; font-weight: bold; font-size: 22px; } /* Position the "next button" to the right */ a.next { right: 0; border-radius: 3px 0 0 3px; } /* On hover, add a black background color with a little bit see-through */ a.prev:hover, a.next:hover { background-color: rgba(0,0,0,0.8); } /* The dots/bullets/indicators */ span.dot { display: inline-block; height: 15px; width: 15px; border-radius: 50%; cursor: pointer; margin: 0 2px; background-color: #bbb; } span.active, span.dot:hover { background-color: #717171; } /* On smaller screens, decrease text size */ @media only screen and (max-width: 500px) { a.prev, a.next, p.text {font-size: 11px} } /* Fading animation */ div.fade { animation-name: fade; animation-duration: 1.5s; } @keyframes fade { from {opacity: 0.4} to {opacity: 1} } </style> <script type="text/javascript"> let slideIndex = 1; function plusSlides(n) { slideIndex += n; showSlides(slideIndex); } function currentSlide(n) { slideIndex = n; showSlides(n); } function showSlides(n) { let slides = document.getElementsByClassName("mySlides"); let dots = document.getElementsByClassName("dot"); if (n > slides.length) {slideIndex = 1} if (n < 1) {slideIndex = slides.length} for (let i = 0; i < slides.length; i++) { slides[i].style.display = "none"; } for (let i = 0; i < dots.length; i++) { dots[i].className = dots[i].className.replace(' active', ""); } slides[slideIndex-1].style.display = "block"; dots[slideIndex-1].className += " active"; } </script> </head> <body> <div class="slideshow-container"> <div class="mySlides fade" style='display:block'> <p class="numbertext">1 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_nature_wide.jpg" style="width:100%"> <p class="text">Nature and sunrise</p> </div> <div class="mySlides fade"> <p class="numbertext">2 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_snow_wide.jpg" style="width:100%"> <p class="text">Snowy Mountains</p> </div> <div class="mySlides fade"> <p class="numbertext">3 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_mountains_wide.jpg" style="width:100%"> <p class="text">Mountains and fjords</p> </div> <div class="mySlides fade"> <p class="numbertext">4 / 4</p> <img src="https://www.broad-network.com/Internet/articles/_general/dir0/images/img_lights_wide.jpg" style="width:100%"> <p class="text">Northern Lights</p> </div> <a class="prev" onclick="plusSlides(-1)">〈</a> <a class="next" onclick="plusSlides(1)">〉</a> </div> <br> <div style="text-align:center"> <span class="dot active" onclick="currentSlide(1)"></span> <span class="dot" onclick="currentSlide(2)"></span> <span class="dot" onclick="currentSlide(3)"></span> <span class="dot" onclick="currentSlide(4)"></span> </div> </body> </html>
The reader should copy the complete code into a text editor. Save the file with any name, but with the extension, .html . Open the file in the browser. Click the right arrow-head several times to see the new images. Click the left arrow-head several times to see the new images. Click some of the dots to see the new images. The images are in the broad-network.com website. So the reader has to be hooked on to the Internet.
Chrys