q8tofq
Last Updated: November 12, 2016
·
55.07K
· guangyi
Cfccdf00acb76649add0727077e3a1c7

Image slider with prev/next button and pager in JavaScript

In me last blog <a href= "https://coderwall.com/p/vsdrug?i=1&p=1&q=&t%5B%5D=%21%21mine&t%5B%5D=%21%21bookmarks"> " How to create image slider with javascirpt" </a>, I programmed a basic image slider.
Following by that, I create an image slider with prev/next button.

Demo: http://cssdeck.com/labs/imageslider-button

Image slider with prev/next button and pager:

Demo: http://cssdeck.com/labs/imageslider-pager

Actually, both of these two programs uses the same core function. I guess this is a good example of code reuse :)

I'm an entry level web developer. with the help of my friend, I got a better understanding of how to design the architecture of the code. But still a lot to learn.

HTML part: pretty much the same as basic slider in last blog:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <script type="text/javascript" src="Image-Slider-LnR.js"></script>
    <link rel="stylesheet" type="text/css" href="Image-Slider-LnR.css">
</head>
<body>
    <div class="container">
        <div class="slider_wrapper">
            <ul id="image_slider">
                <li><img src="./image/1.jpg"></li>
                <li><img src="./image/4.jpg"></li>
                <li><img src="./image/5.jpg"></li>
                <li><img src="./image/4.jpg"></li>
                <li><img src="./image/1.jpg"></li>
                <li><img src="./image/5.jpg"></li>
            </ul>                   
            <span class="nvgt" id="prev"></span>
            <span class="nvgt" id="next"></span>
        </div>
    </div>
</body>
</html>

CSS part: Only the prev/next button part.other parts are the same as basic slider

.nvgt{
    position:absolute;
    top: 120px;
    height: 50px;
    width: 30px;
    opacity: 0.6;
}
.nvgt:hover{
    opacity: 0.9;
}
#prev{
    background: #000 url('./image/prev.png') no-repeat center;
    left: 0px;
}
#next{
    background: #000 url('./image/next.png') no-repeat center;
    right: 0px;
}

The JavsScript part is a little bit different.

Part 1 init()

function init(){
    ul = document.getElementById('image_slider');
    liItems = ul.children;
    imageNumber = liItems.length;
    imageWidth = liItems[0].children[0].clientWidth;
    // set ul's width as the total width of all images in image slider.
    ul.style.width = parseInt(imageWidth * imageNumber) + 'px';
    prev = document.getElementById("prev");
    next = document.getElementById("next");
    /*.onclike = onClickPrev() will be fired when onload; is this because closure?? */
    prev.onclick = function(){ onClickPrev();};
    next.onclick = function(){ onClickNext();};
}

Note:

It's similar to basic slider. When click on prev and next button ,different function will be called.

The reason why designed in this way, is that it will be easier to set boundary and react differently.

If current image is the first image, click the prev button, the slider will go all the way to the last image;

If current image is the last one, click the next button, the slider will go all the way to the first image;

I still didn't figure out why I can't assigned onClickPrev() to prev.onclick directly like this:

prev.onclick = onClickPrev();

Every time I do this, onClickPrev() will be fired when init() is being called. I guess is because of closure, but I'm not sure. I will check it out.

Part 2 onClickPrev(), onClickNext() and slideTo()

/**
* clicking prev. if current image is the first image, ul slide all the way to the last one
* otherwise, it slide to the image on the left of current image.
**/
function onClickPrev(){
    if (currentImage == 0){
        slideTo(imageNumber - 1);
    } 
    else{
        slideTo(currentImage - 1);
    }       
   }

/**
* clicking next. if current image is the last image, ul slide all the way to the first one
* otherwise, it slide to the image on the right of current image.
**/

function onClickNext(){
    if (currentImage == imageNumber - 1){
         slideTo(0);
    }
    else{
        slideTo(currentImage + 1);
    }       
}
/**
* slideTo is the function that actually does the movement.
* it takes one variable--imageToGo as parameter. it's an int stands for the image will be displayed
* By comparing imageToGo and currentImage, it can be decided which direction to move, left or right
* left: direction = -1; right: direction = 1
* so the new left position is the current postion plus/minus (number of imagesToGo * image width)
* when the step function is finished, a callback function will be called to set current image to imageToGo
**/
function slideTo(imageToGo){
    var direction;
    var numOfImageToGo = Math.abs(imageToGo - currentImage);
    direction = currentImage > imageToGo ? 1 : -1;
    currentPosition = -1 * currentImage * imageWidth;
    var opts = {
        duration:1000,
        delta:function(p){return p;},
        step:function(delta){
            ul.style.left = parseInt(currentPosition + direction * delta * imageWidth * numOfImageToGo) + 'px';
        },
        callback:function(){currentImage = imageToGo;}  
    };
    animate(opts);
}

Note:

There is a public variable currentImage, and slideTo accept another variable,imageToGo.
with in slideTo() function, I compare these two variables to decide left or right to slide to, and number of images to pass.

After getting these two key variable, i can slider the image to current position and move the right pixels toward correct direction.

Part 3 generic animation function: just like the basic slider:

function animate(opts){
    var start = new Date;
    var id = setInterval(function(){
        var timePassed = new Date - start;
        var progress = timePassed / opts.duration;
        if (progress > 1){
            progress = 1;
        }
        var delta = opts.delta(progress);
        opts.step(delta);
        if (progress == 1){
            clearInterval(id);
            opts.callback();
         }
    }, opts.delay || 17);
}
window.onload = init;

Yeah~~~ :) Now we have an image slider with prev/next button.

If we want to have pager, just use slideTo()!

I really like this feeling: flexible and easy maintained :)

HTML & CSS: Outside wrapper, ad a ul. Because I want it to be flexible, so there is no code inside ul tag.

<ul id="pager"></ul>

#pager{
    /* firefox has different center method. this doesn't work for fire fox */
    /* not in the center*/
   padding:0px;
    position:relative;
    height:50px;
    margin:auto;
    margin-top:10px;
}
#pager li{
    padding: 0px;
    margin:5px;
    width:20px;
    height:20px;
    border:1px solid white;
    color:white;
    list-style: none;
    opacity: 0.6;
    float:left;
    border-radius: 3px;
    cursor: pointer;
}
#pager li:hover{
    opacity:0.9;
}

JavaScript add one more function, generatePager()

function generatePager(imageNumber){    
    var pageNumber;
    var pagerDiv = document.getElementById('pager');
    for (i = 0; i < imageNumber; i++){
            var li = document.createElement('li');
        pageNumber = document.createTextNode(parseInt(i + 1));
        li.appendChild(pageNumber);
        pagerDiv.appendChild(li);
        // add event inside a loop, closure issue.
        li.onclick = function(i){
                return function(){
                slideTo(i);
            }
        }(i);
    }
    // style.width can't get width from css sheet.  
    var computedStyle = document.defaultView.getComputedStyle(li, null);
    //border width 1px; offsetWidth = 22; offsetWidth returns a number
    var liWidth = li.offsetWidth;
    // remove px from the string returned. like '5px'-->'5'
    var liMargin = parseInt(computedStyle.margin.replace('px',""));
    // margin on both left and right side.
    pagerDiv.style.width = parseInt((liWidth + liMargin * 2) * imageNumber) + 'px';
    }

Here is a thing I want to mention: add event to li within a loop

But this time, li.onclick = function(){slideTo(i); } doesn't work.

I think it's also a closure problem. I will read about closure and post a blog about it later.

Except for the closure issue, I‘m always confused how to organize my code, or the architecture of the code. Do I need to read some books about design pattern? Maybe.

周周加油!

Guangyi Zhou

2 Responses
Add your response

14357
4d58ecfa31ef6affd5defb412cde169a

Thanks for the code
haw can I make it automatical start

over 1 year ago ·
28320

You can define a function like that in javascript

var sequance = function (number) { return number*number;};

var x = sequance(4);// it will return 16

thx alot bro !!

4 months ago ·