[js插件开发教程]实现一个比较完整的开源级选项卡插件

在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>>   [js插件开发教程]原生js仿jquery架构扩展开发选项卡插件.

还缺少两个常用的切换(自动切换与透明度渐变),当然有朋友会说,还有左右,上下等等,这些动画会放在焦点图(幻灯片)插件系列.

(自动切换,停止控制,透明度渐变 ) 效果预览:

[js插件开发教程]实现一个比较完整的开源级选项卡插件

自动切换的实现:

这个思路很简单,开启定时器,让选项卡的索引+1,加到4的时候(选项卡的长度)从0开始

传统做法:

index = 0

index++

if ( index == 4 ) {

index = 0

}

小技巧(估计很多人都没有用过):

var i = ( index + 1 ) %  4

index为当前选中的选项卡 索引

当index = 0,他下一张就是1,  通过上面的取余操作,i = 1

当index = 3,他下一张就是0, 通过上面的取余操作,i = 0

这种方法不需要判断边界,只需要一句代码。在实际开发中,把那个4替换成选项卡的长度

好了,关键的思路和技巧有了,我们开始拼接框架了:

         var defaults = {
contentClass : 'tab-content',
navClass : 'tab-nav',
activeClass : 'active',
triggerElements : '*',
activeIndex : 0,
evType : 'click',
effect : 'none',
auto : false,
delay : 3000,
duration : 1000
};

defaults参数,增加几个配置:

effect: none(没有特效) / fade( 透明度切换 )

auto: false(不会自动切换) / true ( 开启自动切换 )

delay : 多少时间 切换一个选项卡

duration: 透明度开启,这个才会用到,表示,多长时间内 完成透明度的切换

 if ( options.effect == 'fade' ) {
tabContent.style.position = 'relative';
for( var i = 0; i < tabContentEle.length; i++ ) {
tabContentEle[i].style.position = 'absolute';
}
tabContentEle[opt.activeIndex].style.zIndex = _contentLen + 1;
opt.delay += opt.duration;
}

当开启透明度变化的时候,把选项卡元素设置成定位方式,当前选中的选项卡,层级为最高!!! ( 如果不是最高层级,那么默认是最后一个选项卡在最上面,所以 “内容4” 就会在最上层,显然不是我们想要的结果)层级+定位 这一招也很常用,经常用来做显示隐藏,和透明度变化.

根据opt配置,判断是否开启了auto自动切换功能

  //是否自动播放
if ( opt.auto ) {
for( var i = 0 ; i < tabNavEle.length; i++ ){
tabNavEle[i].index = i;
tabNavEle[i].onmouseover = function(){
_api.stop();
_api.setIndex( this.index );
};
tabNavEle[i].onmouseout = function(){
_api.start();
_api.setIndex( this.index );
};
}
_api.start();
}

如果开启了,做两件事情:

1,调用start()函数,让索引+1

2,选项卡导航部分,添加事件控制 自动播放的暂停和开始

start与stop方法?

 _api.stop = function(){
timer && clearInterval( timer );
}; _api.start = function(){
_api.stop();
timer = setInterval( function(){
_api.next();
}, opt.delay );
};

调用next方法,你应该猜得到next方法做的事情就是索引+1 吧

  _api.next = function(){
var i = ( _index + 1 ) % _contentLen;
_api.setIndex( i );
};

索引+1之后,再切换选项卡

最后在setIndex方法:增加透明度变化

 if ( _index != index ) {
tabContentEle[_index].style.zIndex = _contentLen + 1;
for (var i = 0; i < tabContentEle.length; i++) {
if (i != _index) {
tabContentEle[i].style.zIndex = ( index + _contentLen - ( i + 1 ) ) % _contentLen + 1;
tabContentEle[i].style.opacity = 1;
}
}
animate(tabContentEle[_index], {'opacity': 0}, function () {
tabContentEle[_index].style.zIndex = ( index + _contentLen - ( _index + 1 ) ) % _contentLen + 1;
_index = index;
});
}

完整的js代码有220行:

 /**
* Created by ghostwu(吴华).
*/
(function(){
var G = function( selectors, context ){
return new G.fn.init( selectors, context );
}
G.fn = G.prototype = {
length : 0,
constructor : G,
size : function(){
return this.length;
},
init : function( selector, context ){
this.length = 0;
context = context || document;
if ( selector.indexOf( '#' ) == 0 ){
this[0] = document.getElementById( selector.substring( 1 ) );
this.length = 1;
}else {
var aNode = context.querySelectorAll( selector );
for( var i = 0, len = aNode.length; i < len; i++ ) {
this[i] = aNode[i];
}
this.length = len;
}
this.selector = selector;
this.context = context;
return this;
}
} G.fn.init.prototype = G.fn;
G.extend = G.fn.extend = function () {
var i = 1,
len = arguments.length,
dst = arguments[0],
j;
if (dst.length === undefined) {
dst.length = 0;
}
if (i == len) {
dst = this;
i--;
}
for (; i < len; i++) {
for (j in arguments[i]) {
dst[j] = arguments[i][j];
dst.length++;
}
}
return dst;
}; function css(obj, attr, value) {
if (arguments.length == 3) {
obj.style[attr] = value;
} else {
if (obj.currentStyle) {
return obj.currentStyle[attr];
} else {
return getComputedStyle(obj, false)[attr];
}
}
} function animate(obj, attr, fn) {
clearInterval(obj.timer);
var cur = 0;
var target = 0;
var speed = 0;
var start = new Date().getTime();//起始时间
obj.timer = setInterval(function () {
var bFlag = true;
for (var key in attr) {
if (key == 'opacity') {
cur = css(obj, 'opacity') * 100;
} else {
cur = parseInt(css(obj, key));
}
target = attr[key];
speed = ( target - cur ) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (cur != target) {
bFlag = false;
if (key == 'opacity') {
obj.style.opacity = ( cur + speed ) / 100;
obj.style.filter = "alpha(opacity:" + ( cur + speed ) + ")";
} else {
obj.style[key] = cur + speed + "px";
}
}
}
if (bFlag) {
var end = new Date().getTime();//结束时间
console.log( '总计:', ( end - start ) );
clearInterval(obj.timer);
fn && fn.call(obj);
}
}, 30 );
} G.fn.tabs = function( options ){
options = options || {};
var defaults = {
contentClass : 'tab-content',
navClass : 'tab-nav',
activeClass : 'active',
triggerElements : '*',
activeIndex : 0,
evType : 'click',
effect : 'none',
auto : false,
delay : 3000,
duration : 1000
}; var opt = G.extend( {}, defaults, options ); var tabContent = this[0].querySelector( "." + opt.contentClass );
var tabContentEle = tabContent.children;
var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements ); var _contentLen = tabContentEle.length; //选项卡个数
var _index = opt.activeIndex;
var timer = null; if ( options.effect == 'fade' ) {
tabContent.style.position = 'relative';
for( var i = 0; i < tabContentEle.length; i++ ) {
tabContentEle[i].style.position = 'absolute';
}
tabContentEle[opt.activeIndex].style.zIndex = _contentLen + 1;
opt.delay += opt.duration;
} var _api = {}; _api.next = function(){
var i = ( _index + 1 ) % _contentLen;
_api.setIndex( i );
}; _api.stop = function(){
timer && clearInterval( timer );
}; _api.start = function(){
_api.stop();
timer = setInterval( function(){
_api.next();
}, opt.delay );
}; _api.setIndex = function( index ){
//当前标签加上active样式,其余标签删除active样式
for ( var i = 0; i < _contentLen; i++ ) {
if ( tabNavEle[i].classList.contains( 'active' ) ) {
tabNavEle[i].classList.remove('active');
}
}
tabNavEle[index].classList.add( 'active' );
switch ( opt.effect ){
case 'fade':
if ( _index != index ) {
tabContentEle[_index].style.zIndex = _contentLen + 1;
for (var i = 0; i < tabContentEle.length; i++) {
if (i != _index) {
tabContentEle[i].style.zIndex = ( index + _contentLen - ( i + 1 ) ) % _contentLen + 1;
tabContentEle[i].style.opacity = 1;
}
}
animate(tabContentEle[_index], {'opacity': 0}, function () {
tabContentEle[_index].style.zIndex = ( index + _contentLen - ( _index + 1 ) ) % _contentLen + 1;
_index = index;
});
}
break;
default:
for ( var i = 0; i < _contentLen; i++ ) {
tabContentEle[i].style.display = 'none';
}
tabContentEle[index].style.display = 'block';
_index = index;
}
} _api.setIndex( _index ); //默认的选项卡 //所有的标签绑定事件
for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
tabNavEle[i].index = i;
tabNavEle[i].addEventListener( opt.evType, function(){
var i = this.index;
_api.setIndex( i );
}, false );
} //是否自动播放
if ( opt.auto ) {
for( var i = 0 ; i < tabNavEle.length; i++ ){
tabNavEle[i].index = i;
tabNavEle[i].onmouseover = function(){
_api.stop();
_api.setIndex( this.index );
};
tabNavEle[i].onmouseout = function(){
_api.start();
_api.setIndex( this.index );
};
}
_api.start();
} return this;
} var $ = function( selectors, context ){
return G( selectors, context );
}
window.$ = $;
})();
上一篇:Esper系列(九)NamedWindow语法create、Insert、select


下一篇:js删除map中元素