你的位置:首页 > Java教程

[Java教程][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

最终效果: