在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>> [js插件开发教程]原生js仿jquery架构扩展开发选项卡插件.
还缺少两个常用的切换(自动切换与透明度渐变),当然有朋友会说,还有左右,上下等等,这些动画会放在焦点图(幻灯片)插件系列.
(自动切换,停止控制,透明度渐变 ) 效果预览:
自动切换的实现:
这个思路很简单,开启定时器,让选项卡的索引 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替换成选项卡的长度
好了,关键的思路和技巧有了,我们开始拼接框架了:
1 var defaults = {
2 contentClass : 'tab-content',
3 navClass : 'tab-nav',
4 activeClass : 'active',
5 triggerElements : '*',
6 activeIndex : 0,
7 evType : 'click',
8 effect : 'none',
9 auto : false,
10 delay : 3000,
11 duration : 1000
12 };
defaults参数,增加几个配置:
effect: none(没有特效) / fade( 透明度切换 )
auto: false(不会自动切换) / true ( 开启自动切换 )
delay : 多少时间 切换一个选项卡
duration: 透明度开启,这个才会用到,表示,多长时间内 完成透明度的切换
1 if ( options.effect == 'fade' ) {
2 tabContent.style.position = 'relative';
3 for( var i = 0; i < tabContentEle.length; i ) {
4 tabContentEle[i].style.position = 'absolute';
5 }
6 tabContentEle[opt.activeIndex].style.zIndex = _contentLen 1;
7 opt.delay = opt.duration;
8 }
当开启透明度变化的时候,把选项卡元素设置成定位方式,当前选中的选项卡,层级为最高!!! ( 如果不是最高层级,那么默认是最后一个选项卡在最上面,所以 “内容4” 就会在最上层,显然不是我们想要的结果)层级 定位 这一招也很常用,经常用来做显示隐藏,和透明度变化.
根据opt配置,判断是否开启了auto自动切换功能
1 //是否自动播放
2 if ( opt.auto ) {
3 for( var i = 0 ; i < tabNavEle.length; i ){
4 tabNavEle[i].index = i;
5 tabNavEle[i].onmouseover = function(){
6 _api.stop();
7 _api.setIndex( this.index );
8 };
9 tabNavEle[i].onmouseout = function(){
10 _api.start();
11 _api.setIndex( this.index );
12 };
13 }
14 _api.start();
15 }
如果开启了,做两件事情:
1,调用start()函数,让索引 1
2,选项卡导航部分,添加事件控制 自动播放的暂停和开始
start与stop方法?
1 _api.stop = function(){
2 timer && clearInterval( timer );
3 };
4
5 _api.start = function(){
6 _api.stop();
7 timer = setInterval( function(){
8 _api.next();
9 }, opt.delay );
10 };
调用next方法,你应该猜得到next方法做的事情就是索引 1 吧
1 _api.next = function(){
2 var i = ( _index 1 ) % _contentLen;
3 _api.setIndex( i );
4 };
索引 1之后,再切换选项卡
最后在setIndex方法:增加透明度变化
1 if ( _index != index ) {
2 tabContentEle[_index].style.zIndex = _contentLen 1;
3 for (var i = 0; i < tabContentEle.length; i ) {
4 if (i != _index) {
5 tabContentEle[i].style.zIndex = ( index _contentLen - ( i 1 ) ) % _contentLen 1;
6 tabContentEle[i].style.opacity = 1;
7 }
8 }
9 animate(tabContentEle[_index], {'opacity': 0}, function () {
10 tabContentEle[_index].style.zIndex = ( index _contentLen - ( _index 1 ) ) % _contentLen 1;
11 _index = index;
12 });
13 }
完整的js代码有220行:
1 /**
2 * Created by ghostwu(吴华).
3 */
4 (function(){
5 var G = function( selectors, context ){
6 return new G.fn.init( selectors, context );
7 }
8 G.fn = G.prototype = {
9 length : 0,
10 constructor : G,
11 size : function(){
12 return this.length;
13 },
14 init : function( selector, context ){
15 this.length = 0;
16 context = context || document;
17 if ( selector.indexOf( '#' ) == 0 ){
18 this[0] = document.getElementById( selector.substring( 1 ) );
19 this.length = 1;
20 }else {
21 var aNode = context.querySelectorAll( selector );
22 for( var i = 0, len = aNode.length; i < len; i ) {
23 this[i] = aNode[i];
24 }
25 this.length = len;
26 }
27 this.selector = selector;
28 this.context = context;
29 return this;
30 }
31 }
32
33 G.fn.init.prototype = G.fn;
34 G.extend = G.fn.extend = function () {
35 var i = 1,
36 len = arguments.length,
37 dst = arguments[0],
38 j;
39 if (dst.length === undefined) {
40 dst.length = 0;
41 }
42 if (i == len) {
43 dst = this;
44 i--;
45 }
46 for (; i < len; i ) {
47 for (j in arguments[i]) {
48 dst[j] = arguments[i][j];
49 dst.length ;
50 }
51 }
52 return dst;
53 };
54
55 function css(obj, attr, value) {
56 if (arguments.length == 3) {
57 obj.style[attr] = value;
58 } else {
59 if (obj.currentStyle) {
60 return obj.currentStyle[attr];
61 } else {
62 return getComputedStyle(obj, false)[attr];
63 }
64 }
65 }
66
67 function animate(obj, attr, fn) {
68 clearInterval(obj.timer);
69 var cur = 0;
70 var target = 0;
71 var speed = 0;
72 var start = new Date().getTime();//起始时间
73 obj.timer = setInterval(function () {
74 var bFlag = true;
75 for (var key in attr) {
76 if (key == 'opacity') {
77 cur = css(obj, 'opacity') * 100;
78 } else {
79 cur = parseInt(css(obj, key));
80 }
81 target = attr[key];
82 speed = ( target - cur ) / 8;
83 speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
84 if (cur != target) {
85 bFlag = false;
86 if (key == 'opacity') {
87 obj.style.opacity = ( cur speed ) / 100;
88 obj.style.filter = "alpha(opacity:" ( cur speed ) ")";
89 } else {
90 obj.style[key] = cur speed "px";
91 }
92 }
93 }
94 if (bFlag) {
95 var end = new Date().getTime();//结束时间
96 console.log( '总计:', ( end - start ) );
97 clearInterval(obj.timer);
98 fn && fn.call(obj);
99 }
100 }, 30 );
101 }
102
103 G.fn.tabs = function( options ){
104 options = options || {};
105 var defaults = {
106 contentClass : 'tab-content',
107 navClass : 'tab-nav',
108 activeClass : 'active',
109 triggerElements : '*',
110 activeIndex : 0,
111 evType : 'click',
112 effect : 'none',
113 auto : false,
114 delay : 3000,
115 duration : 1000
116 };
117
118 var opt = G.extend( {}, defaults, options );
119
120 var tabContent = this[0].querySelector( "." opt.contentClass );
121 var tabContentEle = tabContent.children;
122 var tabNavEle = this[0].querySelectorAll( "." opt.navClass '>' opt.triggerElements );
123
124 var _contentLen = tabContentEle.length; //选项卡个数
125 var _index = opt.activeIndex;
126 var timer = null;
127
128 if ( options.effect == 'fade' ) {
129 tabContent.style.position = 'relative';
130 for( var i = 0; i < tabContentEle.length; i ) {
131 tabContentEle[i].style.position = 'absolute';
132 }
133 tabContentEle[opt.activeIndex].style.zIndex = _contentLen 1;
134 opt.delay = opt.duration;
135 }
136
137 var _api = {};
138
139 _api.next = function(){
140 var i = ( _index 1 ) % _contentLen;
141 _api.setIndex( i );
142 };
143
144 _api.stop = function(){
145 timer && clearInterval( timer );
146 };
147
148 _api.start = function(){
149 _api.stop();
150 timer = setInterval( function(){
151 _api.next();
152 }, opt.delay );
153 };
154
155 _api.setIndex = function( index ){
156 //当前标签加上active样式,其余标签删除active样式
157 for ( var i = 0; i < _contentLen; i ) {
158 if ( tabNavEle[i].classList.contains( 'active' ) ) {
159 tabNavEle[i].classList.remove('active');
160 }
161 }
162 tabNavEle[index].classList.add( 'active' );
163 switch ( opt.effect ){
164 case 'fade':
165 if ( _index != index ) {
166 tabContentEle[_index].style.zIndex = _contentLen 1;
167 for (var i = 0; i < tabContentEle.length; i ) {
168 if (i != _index) {
169 tabContentEle[i].style.zIndex = ( index _contentLen - ( i 1 ) ) % _contentLen 1;
170 tabContentEle[i].style.opacity = 1;
171 }
172 }
173 animate(tabContentEle[_index], {'opacity': 0}, function () {
174 tabContentEle[_index].style.zIndex = ( index _contentLen - ( _index 1 ) ) % _contentLen 1;
175 _index = index;
176 });
177 }
178 break;
179 default:
180 for ( var i = 0; i < _contentLen; i ) {
181 tabContentEle[i].style.display = 'none';
182 }
183 tabContentEle[index].style.display = 'block';
184 _index = index;
185 }
186 }
187
188 _api.setIndex( _index ); //默认的选项卡
189
190 //所有的标签绑定事件
191 for( var i = 0, len = tabNavEle.length; i < len; i ) {
192 tabNavEle[i].index = i;
193 tabNavEle[i].addEventListener( opt.evType, function(){
194 var i = this.index;
195 _api.setIndex( i );
196 }, false );
197 }
198
199 //是否自动播放
200 if ( opt.auto ) {
201 for( var i = 0 ; i < tabNavEle.length; i ){
202 tabNavEle[i].index = i;
203 tabNavEle[i].onmouseover = function(){
204 _api.stop();
205 _api.setIndex( this.index );
206 };
207 tabNavEle[i].onmouseout = function(){
208 _api.start();
209 _api.setIndex( this.index );
210 };
211 }
212 _api.start();
213 }
214
215 return this;
216 }
217
218 var $ = function( selectors, context ){
219 return G( selectors, context );
220 }
221 window.$ = $;
222 })();