[js插件開發教程]原生js仿jquery架構擴展開發選項卡插件


jquery插件一般是這么干的: $.fn.插件名稱 = function(){}, 把插件的名稱加在.fn上,在源碼里面實際上是擴展到構造函數的原型對象上,如果你沒看過jquery的源代碼,或者你曾經看過,但是不知道為什么把插件擴展到fn上,那么本篇文章就能解答你的疑惑。關於jquery插件開發方式,可以參考我的這篇文章:[js高手之路]jquery插件開發實戰-選項卡詳解

關於選項卡這個功能具體怎么做,不在這里詳解,這個是入門級的功能,本文重在討論插件開發的架構,擴展,以及參數設置。

如果你使用過jquery的選項卡插件,或者其他類型的插件,他們一般都是這么調用的:

$( ".tab" ).tabs( {} )

$(".tab").tabs( function(){} );

一種是傳遞參數定制插件行為

一種是傳遞函數定制插件行為

$(".tab") 選擇到元素,然后返回的是jquery對象

tabs方法擴展在fn上就是擴展都jquery構造函數的原型對象上, 那么對象( $(".tab") )調用原型對象上的方法( tabs )當然就順利成章了。

所以jquery插件擴展本質就是: 構造函數 + 原型對象擴展方法

定義一個構造+原型的方式,下面代碼的原理,我在這篇文章有詳細論述:[js高手之路] 設計模式系列課程 - jQuery的鏈式調用與靈活的構造函數

 1 var G = function( selectors, context ){
 2         return new G.fn.init( selectors, context );
 3     }
 4     G.fn = G.prototype = {
 5         length : 0,
 6         constructor : G,
 7         size : function(){
 8             return this.length;
 9         },
10         init : function( selector, context ){
11             this.length = 0;
12             context = context || document;
13             if ( selector.indexOf( '#' ) == 0 ){
14                 this[0] = document.getElementById( selector.substring( 1 ) );
15                 this.length = 1;
16             }else {
17                 var aNode = context.querySelectorAll( selector );
18                 for( var i = 0, len = aNode.length; i < len; i++ ) {
19                     this[i] = aNode[i];
20                 }
21                 this.length = len;
22             }
23             this.selector = selector;
24             this.context = context;
25             return this;
26         }
27     }
28 
29     G.fn.init.prototype = G.fn;
View Code

接下來,我們還要添加一個插件擴展機制:

 1 G.extend = G.fn.extend = function () {
 2         var i = 1,
 3             len = arguments.length,
 4             dst = arguments[0],
 5             j;
 6         if (dst.length === undefined) {
 7             dst.length = 0;
 8         }
 9         if (i == len) {
10             dst = this;
11             i--;
12         }
13         for (; i < len; i++) {
14             for (j in arguments[i]) {
15                 dst[j] = arguments[i][j];
16                 dst.length++;
17             }
18         }
19         return dst;
20     };
View Code

在這篇文章:[js高手之路] 設計模式系列課程 - jQuery的extend插件機制 有詳細的論述,extend插件擴展機制

像使用jquery一樣暴露接口:

var $ = function( selectors, context ){
return G( selectors, context );
}
window.$ = $;

這個插件的擴展機制和元素選擇機制就完成了,如果要擴展插件,只要在

G.fn上擴展插件的名稱即可,如:

 1 G.fn.tabs = function( options ){
 2     options = options || {};
 3     var defaults = {
 4         contentClass : 'tab-content',
 5         navClass : 'tab-nav',
 6         activeClass : 'active',
 7         triggerElements : '*',
 8         activeIndex : 0,
 9         evType : 'click',
10         effect : 'none'
11     };
12 
13     var opt = G.extend( {}, defaults, options );
14     return this;
15 }

這樣,我們就在G的原型對象上擴展了一個tabs( 選項卡 )插件

options可以定制插件的行為:

contentClass : 'tab-content',     選項卡內容區域的class名稱
navClass : 'tab-nav', 標簽卡區域的class名稱
activeClass : 'active', 標簽卡默認選擇的class名稱:active
triggerElements : '*', 標簽卡默認觸發元素
activeIndex : 0, 默認選中第幾個標簽卡
evType : 'click', 選項卡觸發的事件類型
effect : 'none' 是否有過渡特效:如透明度


var opt = G.extend( {}, defaults, options );

這一段是把定制的配置和默認配置合成到一個對象opt里面,后面的插件行為,就可以根據opt的配置進行定制,這是插件開發參數定制中,常用的一招。

這樣做的好處,可以防止污染默認配置defaults

1  var tabContent = this[0].querySelector( "." + opt.contentClass );
2         var tabContentEle = tabContent.children;
3         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
4 
5         var _contentLen = tabContentEle.length;
6         var _index = opt.activeIndex;

獲取對應的元素。

有了選項卡的元素和配置,我們就開始做業務處理(為所有選項卡添加處理的事件,進行選項卡切換)

定義一個專門的對象_api = {}, 擴展業務api

 1 G.fn.tabs = function( options ){
 2         options = options || {};
 3         var defaults = {
 4             contentClass : 'tab-content',
 5             navClass : 'tab-nav',
 6             activeClass : 'active',
 7             triggerElements : '*',
 8             activeIndex : 0,
 9             evType : 'click',
10             effect : 'none'
11         };
12 
13         var opt = G.extend( {}, defaults, options );
14 
15         var tabContent = this[0].querySelector( "." + opt.contentClass );
16         var tabContentEle = tabContent.children;
17         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
18 
19         var _contentLen = tabContentEle.length;
20         var _index = opt.activeIndex;
21 
22         var _api = {};
23 
24         _api.setIndex = function( index ){
25             //當前標簽加上active樣式,其余標簽刪除active樣式
26             for ( var i = 0; i < _contentLen; i++ ) {
27                 if ( tabNavEle[i].classList.contains( 'active' ) ) {
28                     tabNavEle[i].classList.remove('active');
29                 }
30             }
31             tabNavEle[index].classList.add( 'active' );
32             switch ( opt.effect ){
33                 case 'fade':
34                     break;
35                 default:
36                     for ( var i = 0; i < _contentLen; i++ ) {
37                         tabContentEle[i].style.display = 'none';
38                     }
39                     tabContentEle[index].style.display = 'block';
40                     _index = index;
41             }
42         }
43 
44         _api.setIndex( _index ); //默認的選項卡
45 
46         //所有的標簽綁定事件
47         for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
48             tabNavEle[i].index = i;
49             tabNavEle[i].addEventListener( opt.evType, function(){
50                 var i = this.index;
51                 _api.setIndex( i );
52             }, false );
53         }
54 
55         return this;
56     }
View Code

完整的插件代碼:

  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     G.fn.tabs = function( options ){
 56         options = options || {};
 57         var defaults = {
 58             contentClass : 'tab-content',
 59             navClass : 'tab-nav',
 60             activeClass : 'active',
 61             triggerElements : '*',
 62             activeIndex : 0,
 63             evType : 'click',
 64             effect : 'none'
 65         };
 66 
 67         var opt = G.extend( {}, defaults, options );
 68 
 69         var tabContent = this[0].querySelector( "." + opt.contentClass );
 70         var tabContentEle = tabContent.children;
 71         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
 72 
 73         var _contentLen = tabContentEle.length;
 74         var _index = opt.activeIndex;
 75 
 76         var _api = {};
 77 
 78         _api.setIndex = function( index ){
 79             //當前標簽加上active樣式,其余標簽刪除active樣式
 80             for ( var i = 0; i < _contentLen; i++ ) {
 81                 if ( tabNavEle[i].classList.contains( 'active' ) ) {
 82                     tabNavEle[i].classList.remove('active');
 83                 }
 84             }
 85             tabNavEle[index].classList.add( 'active' );
 86             switch ( opt.effect ){
 87                 case 'fade':
 88                     break;
 89                 default:
 90                     for ( var i = 0; i < _contentLen; i++ ) {
 91                         tabContentEle[i].style.display = 'none';
 92                     }
 93                     tabContentEle[index].style.display = 'block';
 94                     _index = index;
 95             }
 96         }
 97 
 98         _api.setIndex( _index ); //默認的選項卡
 99 
100         //所有的標簽綁定事件
101         for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
102             tabNavEle[i].index = i;
103             tabNavEle[i].addEventListener( opt.evType, function(){
104                 var i = this.index;
105                 _api.setIndex( i );
106             }, false );
107         }
108 
109         return this;
110     }
111 
112     var $ = function( selectors, context ){
113         return G( selectors, context );
114     }
115     window.$ = $;
116 })();
View Code

選項卡布局:

 1 <!DOCTYPE html>
 2 <html>
 3 <head lang="en">
 4     <!--作者:ghostwu(吳華)-->
 5     <meta charset="UTF-8">
 6     <title>選項卡插件 - by ghostwu</title>
 7     <link rel="stylesheet" href="css/tab.css"/>
 8     <script src="./js/tab.js"></script>
 9     <script>
10         window.onload = function () {
11 //            console.log( $(".tab1 .tab-nav li") );
12 //            $( ".tab1" ).tabs( { 'evType' : 'mouseenter' } );
13             $( ".tab1" ).tabs();
14         }
15     </script>
16 </head>
17 <body>
18 <div class="main">
19     <div class="tab tab1">
20         <ul class="tab-nav">
21             <li class="active"><a href="javascript:;">標簽1</a></li>
22             <li><a href="javascript:;">標簽2</a></li>
23             <li><a href="javascript:;">標簽3</a></li>
24             <li><a href="javascript:;">標簽4</a></li>
25         </ul>
26         <div class="tab-content">
27             <p>內容1</p>
28             <p>內容2</p>
29             <p>內容3</p>
30             <p>內容4</p>
31         </div>
32     </div>
33 </div>
34 </body>
35 </html>
View Code

選項卡插件樣式:

 1 * {
 2     margin: 0;
 3     padding: 0;
 4 }
 5 body {
 6     font-size: 14px;
 7     font-family: Tahoma, Verdana,"Microsoft Yahei";
 8 }
 9 a{
10     text-decoration: none;
11     color:#000;
12 }
13 ul,li{
14     list-style-type: none;
15 }
16 img {
17     border:none;
18 }
19 .main {
20     width:960px;
21     margin:20px auto;
22 }
23 .tab{
24     margin: 0 auto 20px;
25 }
26 .tab1 .tab-nav{
27     margin-bottom:8px;
28 }
29 .tab .tab-nav {
30     overflow:hidden;
31 }
32 .tab1 .tab-nav .active{
33     border-bottom:1px solid #000;
34 }
35 .tab1 .tab-nav li {
36     float:left;
37     margin:0 10px;
38 }
39 .tab1 .tab-nav li a {
40     line-height:40px;
41     display:block;
42 }
43 .tab1 .tab-content {
44     height:250px;
45     overflow:hidden;
46 }
47 .tab1 .tab-content p {
48     height:250px;
49     background:#eee;
50 }
View Code

最終效果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM