跟着《beginning jquery》学写slider插件并借助自定义事件改进它
《beginning jquery》是一本很不错的学习jquery的书,作者的讲解深入浅出,很适合初学者,在最后一章里面,作者把前面所有的点结合起来完成了一个轮播图的jquery插件。实现了自动播放,键盘和鼠标控制。但是,借助jquery的自定义事件来优化这个插件,可以使逻辑更清楚。
效果图
首先贴一下作者(Jack Franklin)的原程序:
(function($) {
$.fn.slider = function(options) {
var defaults = {duration: 1000};
var settings = $.extend({}, defaults, options);
return this.each(function() { // store some initial variables
var $slider = $(this);
var $sliderList = $slider.children("ul");
var $sliderItems = $sliderList.children("li");
var $allButtons = $slider.find(".button");
var imageWidth = $sliderItems.first().children("img").width();
var $buttons = {
forward: $allButtons.filter(".forward"),
back: $allButtons.filter(".back")
};
var endMargin = -(($sliderItems.length - 1) * $sliderItems.first().children("img").width());
var animateSlider = function(direction, callback) {
$sliderList.stop(true, true).animate({
"margin-left" : direction + "=" + imageWidth },
settings.duration,
callback);
var increment = (direction === "+" ? -1 : 1);
updateIndex(currentIndex + increment);
};
var animateSliderToMargin = function(margin, callback) {
$sliderList.stop(true, true).animate({"margin-left": margin}, settings.duration, callback);
};
var getLeftMargin = function() {
return parseInt($sliderList.css("margin-left"), 10);
};
var isAtBeginning = function() {
return getLeftMargin() === 0;
};
var isAtEnd = function() {
return getLeftMargin() === endMargin;
};
var updateIndex = function(newIndex) {
currentIndex = newIndex;
$index.text(currentIndex);
};
var triggerSlider = function(direction, callback) {
var isBackButton = (direction === "+");
if(!isBackButton && isAtEnd()) {
animateSliderToMargin(0, callback);
updateIndex(1);
}
else if(isBackButton && isAtBeginning()) {
animateSliderToMargin(endMargin, callback);
updateIndex(totalImages); }
else {
animateSlider(direction, callback);
}
};
$allButtons.on("click", function(event) {
var isBackButton = $(this).hasClass("back");
triggerSlider((isBackButton? "+" : "-"));
event.preventDefault();
});
$(document.documentElement).on("keyup", function(event) {
if(event.keyCode === 37) {
triggerSlider("+");
}
else if (event.keyCode === 39) {
triggerSlider("-");
}
});
});
};
})(jQuery);
这里没有添加键盘事件和自动播放功能,实现了鼠标点击back和forward按钮使图片滚动。当到达最后一张时,点击forward会回到第一张;当第一张图片显示并点击back按钮时,则会显示最后一张,从而实现循环滚动。通过读代码可以看出,作者定义了比较多的函数来实现这个过程。定义的相关函数有:
判断图片位置函数:
isAtBeginning(),
isAtEnd()
DOM函数和动画函数:
animateSliderToMargin(),
updateIndex()
事件处理函数
triggerSlider()
作者的思路是click首先触发图片移动事件,然后再更新索引,但是换一种方式考虑:
1.前进后退两个按钮只控制index的变化。就是只控制一个number的增加和减小。
2.将索引变化看作一个事件,当索引发生变化时相应的margin-left便可以计算出来。
3再通过自定义事件,当索引发生变化时,触发(indexChange)事件,改变slider元素的margin-left值。实现图片的切换。
这样所有逻辑都会在操作index这个数上面,而图片切换只是响应index的变化。这样逻辑上会更清晰,也容易写出更简单紧凑的代码。如下:
(function($) {
$.fn.slider = function(options) {
var defaults = {duration:1000,direction:'left',animationDelay: 5000};
var settings = $.extend({}, defaults, options);
return this.each(function() {
var $slider = $(this),
$sliderList = $slider.children("ul"),
$sliderItems = $sliderList.children("li"),
$itemLen=$sliderItems.length,
$allButtons = $slider.find(".button"),
$forward= $allButtons.filter(".forward"),
$back=$allButtons.filter(".back"),
$counter=$('#counter'),
counter= 1,
type='margin-'+settings.direction,
timer=null,
imageWidth=$sliderItems.find('img').width();
//-----------------------------jquery Map-----------------------------
//-----------------------------utility method-----------------------------
function counterToMargin(count){
return (1-count)*imageWidth;
}
function sliderWay(){
var obj={};
obj[type]=counterToMargin(counter);
return obj;
}
function counterChange(flag){
if(flag==='+'){
counter<$itemLen?(counter++):counter=1;
}
else if(flag==='-'){
counter>1?(counter--):counter=$itemLen;
}
}
var automaticSlide = function() {
timer=setTimeout(function () {
counterChange('+');
$sliderList.trigger('counterChange');
automaticSlide();
},settings.animationDelay);
};
timer =setTimeout(automaticSlide,settings.animationDelay);
var resetTimer = function() {
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(automaticSlide, 30000);
};
//-----------------------------event methods----------------------------
$sliderList.on('counterChange',function(){
resetTimer();
var $this=$(this);
$this.stop(true,true).animate(sliderWay(),settings.duration);
showCounter();
});
$forward.on('click',function(){
counterChange('+');
$sliderList.trigger('counterChange');
});
$back.on('click',function(){
counterChange('-');
$sliderList.trigger('counterChange');
});
$(document.documentElement).on("keyup", function(event) {
if(event.keyCode === 37) { //left arrow
$back.trigger("click"); }
else if (event.keyCode === 39) { //right arrow
$forward.trigger("click"); }
});
//-----------------------------dom methods----------------------------
function showCounter(){
$counter.html(counter);
}
});
};
})(jQuery);
当然,这个插件还远远不够成熟,理想状况下,给出容器元素,在对容器元素调用插件时传入图片url数列就可以实现slider功能,才算一个比较成熟的插件。这里只是提供一种组织代码思路。
demo
cnblogs-md-editor编辑器,用Markdown写博客就用它