2
\$\begingroup\$

This is my first jQuery plugin. It is a simple slider that requires very little mark up in html. It works for my purposes but I am not a jQuery expert and I am wondering if there are mistakes or shortcuts that I took in its creation.

Thank you for taking a look at the plugin!

The HTML

<script type="text/javascript">
    $(document).ready(function(){
        $('.st-slider').stSlider();
    });
</script>

<div id="feature-slider" class="st-slider">
      <ul>
           <li data-collection-name="Collection 1"><a href="#"><img src="images/slide1.jpg"></a></li>
           <li data-collection-name="Collection 2"><a href="#"><img src="images/slide2.jpg"></a></li>
           <li data-collection-name="Collection 3"><a href="#"><img src="images/slide3.jpg"></a></li>
           <li data-collection-name="Collection 4"><a href="#"><img src="images/slide4.jpg"></a></li>
      </ul>
</div>

The CSS

.st-slider {
    overflow: hidden;
    width: 685px;
    height: 412px;
    margin-top: 15px;
    position: relative;
    margin-bottom: 70px;
}
.st-slider ul {
    margin: 0;
    padding: 0;
    position: relative;
}

.st-slider ul li {
    display: block;
    margin: 0;
    padding: 0;
    background: #fff;
    float: left;
    padding: 5px;
    border: 1px solid #ddd;
}
.st-slider .st-slider-caption {
    background: rgba(215,204,186,0.8);
    font-size: 18px;
    color: #fff;
    text-transform: uppercase;
    letter-spacing: .3em;
    position: relative;
    z-index: 2;
    text-align: center;
    height: 40px;
    top: 280px;
    width: 685px;
}
.st-slider .st-slider-caption span {
    position: absolute;
    top: 9px;
    left: 0;
    text-align: center;
    width: 685px;
}
.st-slider .st-slider-nav {
    position: relative;
    top: 229px;
    z-index: 3;
    height: 62px;
    width: 100%;
}
.st-slider .st-slider-nav div {
    background: url('../images/st-slider-nav.png');
    width: 30px;
    height: 62px;   
    position: absolute;
    cursor: pointer;
}
.st-slider .st-slider-nav .next {
    background-position: -30px 0;
    left: 655px;
}   
.st-slider .st-slider-nav .prev:hover {
    background-position: -60px 0;   
}
.st-slider .st-slider-nav .next:hover {
    background-position: -90px 0;   
} 
.st-slider-go-to-nav {
    display: block;
    margin-top: -55px;
    padding-top: 0;
    font-size: 12px;
    color: #aaa;
    float: right;
}
.st-slider-go-to-nav {
    list-style: none;
}
.st-slider-go-to-nav li {
    padding: 0;
    margin-right: 10px;
    float: left;
}
.st-slider-go-to-nav li:last-child {
    margin-right: 0;
}
.st-slider-go-to-nav li a {
    padding: 5px 8px;
    color: #aaa;
    text-decoration: none;
    cursor: pointer;
}
.st-slider-go-to-nav li a:hover, .st-slider-go-to-nav li.current a {
    color: #666;
    background: #eee;
}

The jQuery

(function( $ ) {

$.fn.stSlider = function() {


    var sliderContainer = $(this),
        slidePane       = sliderContainer.find('ul'),
        cloneFirst      = slidePane.find('li:first-child').clone(),
        cloneLast       = slidePane.find('li:last-child').clone();

    cloneFirst.attr('data-clone', 'last').appendTo(slidePane);
    cloneLast.attr('data-clone', 'first').prependTo(slidePane);


    var slides = slidePane.find('li');

    var slideWidth     = $(this).width(),
        negSlideWidth  = 0 - slideWidth,
        slidesArray    = $.makeArray(slides),
        slidePaneWidth = ( slidesArray.length  * slideWidth );

    // Build slider controls
    var captionHolderDiv = $('<div class="st-slider-caption"></div>'),
        captionDiv       = $('<span></span> '),
        navDiv           = $('<div class="st-slider-nav"></div>'),
        prevDiv          = $('<div class="prev"></div>'),
        nextDiv          = $('<div class="next"></div>'),
        goToNav          = $('<ul class="st-slider-go-to-nav"></ul>');

    // Insert slider controls into DOM
    slidePane.after(captionHolderDiv);
    captionDiv.appendTo(captionHolderDiv);
    captionHolderDiv.after(navDiv);
    prevDiv.appendTo(navDiv);
    nextDiv.appendTo(navDiv);
    sliderContainer.after(goToNav);

    // Build and insert each slide number link
    for(var i = 0; i < slidesArray.length - 2; i++) {
        var count = i + 1;
        var goToNavItem = $('<li id="' + count + '"><a>' + count + '</a></li>');

        goToNavItem.appendTo(goToNav);

        if ( count ==  1 ) {
            goToNavItem.addClass('current');
        }
    }

    var slideCaptionHolder = sliderContainer.find('.st-slider-caption span'),
        slideCaption       = sliderContainer.find('li:nth-child(2)').data('collection-name'),
        goToArray          = $('.st-slider-go-to-nav li');

    slidePane.width(slidePaneWidth);
    slides.addClass('st-slider-slide');
    slidePane.animate({left: negSlideWidth}, 0);

    slideCaptionHolder.html(slideCaption);


    function slide(direction, location) {

        var x            = parseInt(slidePane.css('left'), 10),
            y            = Math.abs(x),
            i            = 0,
            theSlide     = slides.eq(i),
            slideCaption = theSlide.data('collection-name'),
            slideClone   = theSlide.data('clone'),
            lastPosition = 0 - slidePaneWidth + (slideWidth * 2);

        if ( direction == 'null' ) { }

        else if (direction == 'prev') {

            i = (y / slideWidth) -1;
            i = i % slidesArray.length;

            theSlide     = slides.eq(i);
            slideCaption = theSlide.data('collection-name');
            slideClone   = theSlide.data('clone');

            slidePane.animate({left: parseInt(slidePane.css('left'), 10) + slideWidth}, 600);

            slideCaptionHolder.fadeOut(300, function() {
                $(this).html(slideCaption);
                $(this).fadeIn(300, function(){
                    if ( slideClone && slideClone == 'first' ) {
                        slidePane.animate({left: lastPosition}, 0);
                    }
                });
            });

            goToArray.removeClass('current');

            if ( slideClone == 'last') {
                goToArray.eq(slidesArray.length - 2).addClass('current');
            } else {
                goToArray.eq(i - 1).addClass('current');
            }

        }

        else {

            i = (y / slideWidth) +1;
            i = i % slidesArray.length;

            theSlide     = slides.eq(i);
            slideCaption = theSlide.data('collection-name');
            slideClone   = theSlide.data('clone');

            slidePane.animate({left: parseInt(slidePane.css('left'), 10) - slideWidth }, 600);

            slideCaptionHolder.fadeOut(300, function() {
                $(this).html(slideCaption);
                $(this).fadeIn(300, function(){
                    if ( slideClone && slideClone == 'last' ) {
                        slidePane.animate({left: negSlideWidth }, 0);
                    }
                });
            });

            goToArray.removeClass('current');

            if ( slideClone == 'last') {
                goToArray.eq(0).addClass('current');
            } else {
                goToArray.eq(i - 1).addClass('current');
            }
        }


        if ( location ) {

            i = location;

            theSlide        = slides.eq(i);
            slideCaption = theSlide.data('collection-name');
            slideClone   = theSlide.data('clone');

            slidePane.animate({left: 0 - (i * slideWidth) }, 600);

            slideCaptionHolder.fadeOut(300, function() {
                $(this).html(slideCaption);
                $(this).fadeIn(300, function(){
                    if ( slideClone && slideClone == 'last' ) {
                        slidePane.animate({left: negSlideWidth }, 0);
                    } else if ( slideClone && slideClone == 'first' ) {
                        slidePane.animate({left: lastPosition}, 0);
                    }
                });
            });
                goToArray.removeClass('current');
                goToArray.eq(i - 1).addClass('current');
        }

    }

    timer = setInterval(slide, 5000);


    sliderContainer.hover(function(){
        clearInterval(timer);
    }, function(){
        timer = setInterval(slide, 5000);
    });


    var slidePrev = sliderContainer.find('.prev'),
        slideNext = sliderContainer.find('.next');

    slidePrev.click(function(){
        slide('prev');
    });

    slideNext.click(function(){
        slide();
    });

    $('.st-slider-go-to-nav li a').click(function(){

        if ( $(this).parent().hasClass('current') ) {

        } else {
            var location = $(this).parent().attr('id');

            slide('null', location);

            clearInterval(timer);
            timer = setInterval(slide, 5000);
        }

    });
};

})( jQuery );
\$\endgroup\$
1
\$\begingroup\$

Your code seems solid to me, only a few things my eye caught:

  • you're using $(this) in multiple places, while your sliderContainer already holds that jQuery object. This object can be then reused instead of making jQuery convert DOM into jQuery several times
  • instead of writing cloneFirst.attr('data-clone', 'last').appendTo(slidePane); it's advised to use jQuery's data() function to set data for DOM elements: cloneFirst.data('clone', 'last').appendTo(slidePane);
\$\endgroup\$
  • \$\begingroup\$ Wow! First of all, thank you so much for the feedback! Second, thank you for the advice on using $(this). As for using .data(), I tried that first but it did not seem to create the data-collection-name attribute in the html since it wasn't already there. Perhaps I missed something? \$\endgroup\$ – Rob Chipman Oct 6 '12 at 21:44
  • \$\begingroup\$ interesting remark with regards to the data() function... if that didn't work, perhaps it would be wise to ask/report this to jQuery forums / bug tracker? \$\endgroup\$ – Zathrus Writer Oct 6 '12 at 21:55
  • \$\begingroup\$ This plugin does not seem to work when multiple instaces of the function is called on $(document).ready();. I am guessing I will need to loop through each function call to maintain the identity of the one that is being executed? I am not sure. It doesn't particularly matter for this project but it would be nice to have an idea if in the future I needed to expand to multiple instances. \$\endgroup\$ – Rob Chipman Oct 6 '12 at 22:03
  • \$\begingroup\$ I'm sorry, but I cannot answer that question... I have never done a jQuery plugin and I was merely reviewing the code for performance and best practices issues... perhaps you can try and ask on StackOverflow if you need to resolve the issue instead? \$\endgroup\$ – Zathrus Writer Oct 6 '12 at 22:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.