jquery插件一般是這麼乾的: $.fn.插件名稱 = function(){}, 把插件的名稱加在.fn上,在源碼裡面實際上是擴展到構造函數的原型對象上,如果你沒看過jquery的源代碼,或者你曾經看過,但是不知道為什麼把插件擴展到fn上,那麼本篇文章就能解答你的疑惑。關於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
最終效果: