zoukankan      html  css  js  c++  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

    最终效果:

  • 相关阅读:
    char与unsigned char的区别
    C语言 —— sprintf()函数
    char *s 与 char s[ ]的区别
    打印不同对象的字节表示 ( 对int*强制转换成unsigned char*的理解 )
    洛谷P2242 公路维修问题(Road)
    洛谷P1209 [USACO1.3]修理牛棚 Barn Repair
    洛谷P1345 [USACO5.4]奶牛的电信Telecowmunication
    洛谷P2246 SAC#1
    Bzoj4300 绝世好题
    Uva1398 Meteor
  • 原文地址:https://www.cnblogs.com/ghostwu/p/7700288.html
Copyright © 2011-2022 走看看