(For an answer using the OP's original code, see my second answer.)
Okay.. so I may have gone a little overboard:)
The thing is, when I just started out I did exactly the same as you did, with a bunch of variables to store the progress and current position etc.
But really, all that isn't necessary, and JavaScript is quite unique in that way because of the 'physical' DOM structure that is already present on the page and can be referenced instead of using a dedicated variable. They can in many cases function as your 'variables', and in many cases are preferred over (global) variables.
ANYWAY, so I pretty much changed your whole code, also renamed probably everything in the process, stripped out unnecessary elements like the <a>
tags. You might have a need for them, but I wanted to make the code as clean as possible so it's easier to understand:
Read the comments in the code to understand what's going on.
Ignore the CSS, it's only for styling. All the functionality is in the JS.
PURE JS:
window.onload = function() {
function changeSlide(d, id) {
var slides = document.getElementById(id).children[0];
for (var i=0,count=slides.children.length; i<count; ++i) {
if (slides.children[i].className.indexOf("active") != -1) {var slide=slides.children[i]; break;} //store the current slide
}
if (d=="next") {
if (slides.lastElementChild.className.indexOf("active") != -1) {
slides.firstElementChild.className += " active"; //loop around and activate the first slide
} else {slide.nextElementSibling.className += " active";} //activate the next slide
} else { //prev
if (slides.firstElementChild.className.indexOf("active") != -1) {
slides.lastElementChild.className += " active"; //loop around and activate the last slide
} else {slide.previousElementSibling.className += " active";} //activate the prev slide
}
slide.className = slide.className.replace(" active",""); //deactivate the current slide
}
//AUTOMATIC LOOP------------------------------
function timer(id){setTimeout(function(){if (document.getElementById(id).className.indexOf("auto") != -1) {changeSlide("next",id); timer(id);}},4000);}
for (var i=0,s=document.getElementsByClassName("slider"),count=s.length; i<count; ++i) {timer(s[i].id);} //create a timer-loop for every slider
//EVENT-HANDLERS------------------------------
for (var i=0,s=document.getElementsByClassName("slider"),count=s.length; i<count; ++i) {
s[i].onmousedown = function(){return false;}; //otherwise sometimes the space next to the slider gets selected on navigation
//click-handlers for prev/next buttons
for (var j=0,c=s[i].children.length; j<c; ++j) {
if (s[i].children[j].className.indexOf("nav") != -1) {
s[i].children[j].onclick = function() {
var s = document.getElementById(this.parentNode.id);
if (s.className.indexOf("auto") != -1) {s.className = s.className.replace(" auto","");} //stop the automatic loop
changeSlide(this.className.split(" ")[1],this.parentNode.id); //prev/next slide ('this.className.split(" ")[1]' returns the second classname)
};
}
}
}
};
.slider {display:inline-block; position:relative; width:240px; height:135px; background:#000;}
.slider .slides {width:100%; height:100%;}
.slider .slides .slide {float:left; width:0; height:100%; background:center center/contain no-repeat; transition:width 0.5s ease 0s;}
.slider .slides .slide.active {width:100%;}
.slider .nav {position:absolute; top:0; width:15%; min-width:44px; height:100%; background:#888; opacity:0; cursor:pointer;}
.slider:hover .nav {opacity:0.3;}
.slider .nav.prev {left:0;}
.slider .nav.next {right:0;}
.slider .nav .btn {position:absolute; top:0; bottom:0; width:34px; height:34px; margin:auto 0; box-sizing:border-box; border:2px solid #ddd; border-radius:999px; background:#555; opacity:0.7; visibility:hidden;}
.slider .nav.prev .btn {left:5px;}
.slider .nav.next .btn {right:5px;}
.slider:hover .nav .btn {visibility:visible;}
.slider:hover .nav .btn:hover {border-color:#fff; opacity:1;}
.slider .nav .btn .arrow {position:absolute; top:0; bottom:0; width:0; height:0; margin:auto 0; border:10px solid transparent;}
.slider .nav.prev .btn .arrow {right:calc(50% - 3px); border-right-color:#ddd;}
.slider .nav.next .btn .arrow {left:calc(50% - 3px); border-left-color:#ddd;}
.slider .nav.prev .btn:hover .arrow {border-right-color:#fff;}
.slider .nav.next .btn:hover .arrow {border-left-color:#fff;}
<div class="slider auto" id="slider1">
<div class="slides">
<div class="slide active" style="background-image:url('https://placeimg.com/320/180/arch');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/250/animals');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/720/450/tech/sepia');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/480/480/nature');"></div>
</div>
<div class="nav prev"><div class="btn"><div class="arrow"></div></div></div>
<div class="nav next"><div class="btn"><div class="arrow"></div></div></div>
</div>
<div class="slider auto" id="slider2">
<div class="slides">
<div class="slide active" style="background-image:url('https://placeimg.com/320/480/nature');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/480/people/sepia');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/180/tech');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/360/arch/sepia');"></div>
</div>
<div class="nav prev"><div class="btn"><div class="arrow"></div></div></div>
<div class="nav next"><div class="btn"><div class="arrow"></div></div></div>
</div>
jsfiddle: https://jsfiddle.net/9pommada/9/
- I made it so that you can add multiple sliders to your page and they will all work independently from each other. I did that by removing (almost) all
id
s and working only with classes.
The only id
s that are left are on the .slider
s, I couldn't get it to work in pure JS without them.
If you want no id
s at all, check out the jQuery version below.
- The for-loop may look a little weird to you. Basically, instead of doing the more familiar version
for (var i=0; i<slides.children.length; i++) {
, I declare an extra variable in the first part of the for-loop, so that the length
isn't calculated on every iteration. The result is the following for (var i=0,count=slides.children.length; i<count; ++i) {
. Using ++i
has a subtle difference over i++
, but used like this there is no practical difference.
- I said all the functionality is in the JS, that's not entirely true. There are some things in CSS that have a great impact on the way the slider behaves. Although it is mostly fancy transition, no too long ago you would have had to code that in JavaScript too, so it is definitely part of the functionality I think.
One is the transition:width 0.5s ease 0s;
. Together with float:left;
it makes the slides really slide, instead of just static replacement.
The other is the background:center center/contain no-repeat;
. This makes that the images are always nicely centered and scaled to fit the container. If the image is too wide it gets vertically centered, if it's too tall it gets horizontally centered. That's all CSS.
- Another important point you may overlook at first, is that for the point above (about the image centering and scaling) to work, you need to replace your
<img/>
tags by <div>
s, and on those <div>
s set the images as background-images
.
I did it in HTML here, this is usually considered bad practice, but if you would move these to CSS, you would have to give every slide either a unique class
or an id
for that alone.
JQUERY:
$(function() {
function changeSlide(d, slides) {
var slide = $(slides).children(".slide.active")[0]; //store the current slide
if (d=="next") {
if ($(slides).children(".slide").last().hasClass("active")) {
$(slides).children(".slide").first().addClass("active"); //loop around and activate the first slide
} else {$(slide).next().addClass("active");} //activate the next slide
} else { //prev
if ($(slides).children(".slide").first().hasClass("active")) {
$(slides).children(".slide").last().addClass("active"); //loop around and activate the last slide
} else {$(slide).prev().addClass("active");} //activate the prev slide
}
$(slide).removeClass("active"); //deactivate the current slide
}
//AUTOMATIC LOOP------------------------------
function timer(slides){setTimeout(function(){if ($(slides).parent().hasClass("auto")) {changeSlide("next",slides); timer(slides);}},4000);}
$(".slider").each(function(){timer($(this).children(".slides")[0]);}); //create a timer-loop for every slider
//EVENT-HANDLERS------------------------------
$(".slider").on("mousedown",function(){return false;}); //otherwise sometimes the space next to the slider gets selected on navigation
$(".slider .nav").click(function(){
if ($(this).closest(".slider").hasClass("auto")) {$(this).closest(".slider").removeClass("auto");}; //stop the automatic loop
changeSlide(this.className.split(" ")[1],$(this).siblings(".slides")[0]); //prev/next slide
});
});
.slider {display:inline-block; position:relative; width:240px; height:135px; background:#000;}
.slider .slides {width:100%; height:100%;}
.slider .slides .slide {float:left; width:0; height:100%; background:center center/contain no-repeat; transition:width 0.5s ease 0s;}
.slider .slides .slide.active {width:100%;}
.slider .nav {position:absolute; top:0; width:15%; min-width:44px; height:100%; background:#ddd; opacity:0; cursor:pointer;}
.slider:hover .nav {opacity:0.3;}
.slider .nav.prev {left:0;}
.slider .nav.next {right:0;}
.slider .nav .btn {position:absolute; top:0; bottom:0; width:34px; height:34px; margin:auto 0; box-sizing:border-box; border:2px solid #ddd; border-radius:999px; background:#555; opacity:0.7; visibility:hidden;}
.slider .nav.prev .btn {left:5px;}
.slider .nav.next .btn {right:5px;}
.slider:hover .nav .btn {visibility:visible;}
.slider:hover .nav:hover .btn {opacity:1;}
.slider .nav .btn .arrow {position:absolute; top:0; bottom:0; width:0; height:0; margin:auto 0; border:10px solid transparent;}
.slider .nav.prev .btn .arrow {right:calc(50% - 3px); border-right-color:#ddd;}
.slider .nav.next .btn .arrow {left:calc(50% - 3px); border-left-color:#ddd;}
.slider .nav.prev:hover .btn .arrow {border-right-color:#fff;}
.slider .nav.next:hover .btn .arrow {border-left-color:#fff;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="slider auto">
<div class="slides">
<div class="slide active" style="background-image:url('https://placeimg.com/320/180/arch');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/250/animals');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/720/450/tech/sepia');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/480/480/nature');"></div>
</div>
<div class="nav prev"><div class="btn"><div class="arrow"></div></div></div>
<div class="nav next"><div class="btn"><div class="arrow"></div></div></div>
</div>
<div class="slider auto">
<div class="slides">
<div class="slide active" style="background-image:url('https://placeimg.com/320/480/nature');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/480/people/sepia');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/180/tech');"></div>
<div class="slide" style="background-image:url('https://placeimg.com/640/360/arch/sepia');"></div>
</div>
<div class="nav prev"><div class="btn"><div class="arrow"></div></div></div>
<div class="nav next"><div class="btn"><div class="arrow"></div></div></div>
</div>
jsfiddle: https://jsfiddle.net/nwbx5g02/11/
- Because we can work with jQuery objects, we can eliminate all
id
s, which makes it more flexible to implement on other sites.
Which one you choose probably comes mostly down to preference. Personally I prefer jQuery, 'cause it just makes life so much easier and I'm already using it for everything else anyway. But when I just started out I was really suspicious of it, and coding in pure JS certainly helped me get a good understanding of what I was doing. Anyways, hope I could help, if you have any questions about the code just leave 'em in the comments and I'll try to answer them.