在這篇文章中,我實現了一個基本的選項卡功能:請猛擊后面的鏈接>> [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 })();