[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的链式调用与灵活的构造函数
 var G = function( selectors, context ){
         return new G.fn.init( selectors, context );
     }
     G.fn = G.prototype = {
         length : 0,
         constructor : G,
         size : function(){
             return this.length;
         },
         init : function( selector, context ){
             this.length = 0;
             context = context || document;
             if ( selector.indexOf( '#' ) == 0 ){
                 this[0] = document.getElementById( selector.substring( 1 ) );
                 this.length = 1;
             }else {
                 var aNode = context.querySelectorAll( selector );
                 for( var i = 0, len = aNode.length; i < len; i++ ) {
                     this[i] = aNode[i];
                 }
                 this.length = len;
             }
             this.selector = selector;
             this.context = context;
             return this;
         }
     }
     G.fn.init.prototype = G.fn;
接下来,我们还要添加一个插件扩展机制:
 G.extend = G.fn.extend = function () {
         var i = 1,
             len = arguments.length,
             dst = arguments[0],
             j;
         if (dst.length === undefined) {
             dst.length = 0;
         }
         if (i == len) {
             dst = this;
             i--;
         }
         for (; i < len; i++) {
             for (j in arguments[i]) {
                 dst[j] = arguments[i][j];
                 dst.length++;
             }
         }
         return dst;
     };
在这篇文章:[js高手之路] 设计模式系列课程 - jQuery的extend插件机制 有详细的论述,extend插件扩展机制
像使用jquery一样暴露接口:
var $ = function( selectors, context ){
    return G( selectors, context );
}
window.$ = $;
这个插件的扩展机制和元素选择机制就完成了,如果要扩展插件,只要在
G.fn上扩展插件的名称即可,如:
 G.fn.tabs = function( options ){
     options = options || {};
     var defaults = {
         contentClass : 'tab-content',
         navClass : 'tab-nav',
         activeClass : 'active',
         triggerElements : '*',
         activeIndex : 0,
         evType : 'click',
         effect : 'none'
     };
     var opt = G.extend( {}, defaults, options );
     return this;
 }
这样,我们就在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
var tabContent = this[0].querySelector( "." + opt.contentClass );
var tabContentEle = tabContent.children;
var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements ); var _contentLen = tabContentEle.length;
var _index = opt.activeIndex;
获取对应的元素。
有了选项卡的元素和配置,我们就开始做业务处理(为所有选项卡添加处理的事件,进行选项卡切换)
定义一个专门的对象_api = {}, 扩展业务api
 G.fn.tabs = function( options ){
         options = options || {};
         var defaults = {
             contentClass : 'tab-content',
             navClass : 'tab-nav',
             activeClass : 'active',
             triggerElements : '*',
             activeIndex : 0,
             evType : 'click',
             effect : 'none'
         };
         var opt = G.extend( {}, defaults, options );
         var tabContent = this[0].querySelector( "." + opt.contentClass );
         var tabContentEle = tabContent.children;
         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
         var _contentLen = tabContentEle.length;
         var _index = opt.activeIndex;
         var _api = {};
         _api.setIndex = function( index ){
             //当前标签加上active样式,其余标签删除active样式
             for ( var i = 0; i < _contentLen; i++ ) {
                 if ( tabNavEle[i].classList.contains( 'active' ) ) {
                     tabNavEle[i].classList.remove('active');
                 }
             }
             tabNavEle[index].classList.add( 'active' );
             switch ( opt.effect ){
                 case 'fade':
                     break;
                 default:
                     for ( var i = 0; i < _contentLen; i++ ) {
                         tabContentEle[i].style.display = 'none';
                     }
                     tabContentEle[index].style.display = 'block';
                     _index = index;
             }
         }
         _api.setIndex( _index ); //默认的选项卡
         //所有的标签绑定事件
         for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
             tabNavEle[i].index = i;
             tabNavEle[i].addEventListener( opt.evType, function(){
                 var i = this.index;
                 _api.setIndex( i );
             }, false );
         }
         return this;
     }
完整的插件代码:
/**
* Created by ghostwu(吴华).
*/
(function(){
var G = function( selectors, context ){
return new G.fn.init( selectors, context );
}
G.fn = G.prototype = {
length : 0,
constructor : G,
size : function(){
return this.length;
},
init : function( selector, context ){
this.length = 0;
context = context || document;
if ( selector.indexOf( '#' ) == 0 ){
this[0] = document.getElementById( selector.substring( 1 ) );
this.length = 1;
}else {
var aNode = context.querySelectorAll( selector );
for( var i = 0, len = aNode.length; i < len; i++ ) {
this[i] = aNode[i];
}
this.length = len;
}
this.selector = selector;
this.context = context;
return this;
}
} G.fn.init.prototype = G.fn;
G.extend = G.fn.extend = function () {
var i = 1,
len = arguments.length,
dst = arguments[0],
j;
if (dst.length === undefined) {
dst.length = 0;
}
if (i == len) {
dst = this;
i--;
}
for (; i < len; i++) {
for (j in arguments[i]) {
dst[j] = arguments[i][j];
dst.length++;
}
}
return dst;
}; G.fn.tabs = function( options ){
options = options || {};
var defaults = {
contentClass : 'tab-content',
navClass : 'tab-nav',
activeClass : 'active',
triggerElements : '*',
activeIndex : 0,
evType : 'click',
effect : 'none'
}; var opt = G.extend( {}, defaults, options ); var tabContent = this[0].querySelector( "." + opt.contentClass );
var tabContentEle = tabContent.children;
var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements ); var _contentLen = tabContentEle.length;
var _index = opt.activeIndex; var _api = {}; _api.setIndex = function( index ){
//当前标签加上active样式,其余标签删除active样式
for ( var i = 0; i < _contentLen; i++ ) {
if ( tabNavEle[i].classList.contains( 'active' ) ) {
tabNavEle[i].classList.remove('active');
}
}
tabNavEle[index].classList.add( 'active' );
switch ( opt.effect ){
case 'fade':
break;
default:
for ( var i = 0; i < _contentLen; i++ ) {
tabContentEle[i].style.display = 'none';
}
tabContentEle[index].style.display = 'block';
_index = index;
}
} _api.setIndex( _index ); //默认的选项卡 //所有的标签绑定事件
for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
tabNavEle[i].index = i;
tabNavEle[i].addEventListener( opt.evType, function(){
var i = this.index;
_api.setIndex( i );
}, false );
} return this;
} var $ = function( selectors, context ){
return G( selectors, context );
}
window.$ = $;
})();
选项卡布局:
<!DOCTYPE html>
<html>
<head lang="en">
<!--作者:ghostwu(吴华)-->
<meta charset="UTF-8">
<title>选项卡插件 - by ghostwu</title>
<link rel="stylesheet" href="css/tab.css"/>
<script src="./js/tab.js"></script>
<script>
window.onload = function () {
// console.log( $(".tab1 .tab-nav li") );
// $( ".tab1" ).tabs( { 'evType' : 'mouseenter' } );
$( ".tab1" ).tabs();
}
</script>
</head>
<body>
<div class="main">
<div class="tab tab1">
<ul class="tab-nav">
<li class="active"><a href="javascript:;">标签1</a></li>
<li><a href="javascript:;">标签2</a></li>
<li><a href="javascript:;">标签3</a></li>
<li><a href="javascript:;">标签4</a></li>
</ul>
<div class="tab-content">
<p>内容1</p>
<p>内容2</p>
<p>内容3</p>
<p>内容4</p>
</div>
</div>
</div>
</body>
</html>
选项卡插件样式:
 * {
     margin:;
     padding:;
 }
 body {
     font-size: 14px;
     font-family: Tahoma, Verdana,"Microsoft Yahei";
 }
 a{
     text-decoration: none;
     color:#000;
 }
 ul,li{
     list-style-type: none;
 }
 img {
     border:none;
 }
 .main {
     width:960px;
     margin:20px auto;
 }
 .tab{
     margin: 0 auto 20px;
 }
 .tab1 .tab-nav{
     margin-bottom:8px;
 }
 .tab .tab-nav {
     overflow:hidden;
 }
 .tab1 .tab-nav .active{
     border-bottom:1px solid #000;
 }
 .tab1 .tab-nav li {
     float:left;
     margin:0 10px;
 }
 .tab1 .tab-nav li a {
     line-height:40px;
     display:block;
 }
 .tab1 .tab-content {
     height:250px;
     overflow:hidden;
 }
 .tab1 .tab-content p {
     height:250px;
     background:#eee;
 }
最终效果:

[js插件开发教程]原生js仿jquery架构扩展开发选项卡插件的更多相关文章
- [js插件开发教程]实现一个比较完整的开源级选项卡插件
		在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>> [js插件开发教程]原生js仿jquery架构扩展开发选项卡插件. 还缺少两个常用的切换(自动切换与透明度渐变),当然 ... 
- [js插件开发教程]一步步开发一个可以定制配置的隔行变色小插件
		隔行变色功能,不用js,直接用css伪类就可以做,这个实例可以作为js插件开发很好的入门级实例.本文实现的隔行变色包括以下功能: 1,支持2种常用结构共存( div元素 和 表格类型 ) 2,一个页面 ... 
- js 小工具-- 原生 js 去除空格
		// 原生js 去除字符串空格 <script type="text/javascript"> String.prototype.trim = function (){ ... 
- 编写一个jQuery的扩展方法(插件)
		<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ... 
- [js插件开发教程]定制一个手风琴插件(accordion)
		本文带来一个垂直方向的手风琴插件开发,可以定制的功能如下: contentClass : 'panel', //面板样式navClass : 'nav', //导航样式activeClass : 'a ... 
- js中的原生Ajax和JQuery中的Ajax
		AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML). js中的Ajax: 参数介绍: open(String method,Str ... 
- js设置cookie(原生js)
		cookie 与 session 是网页开发中常用的信息存储方式.Cookie是在客户端开辟的一块可存储用户信息的地方:Session是在服务器内存中开辟的一块存储用户信息的地方. JavaScrip ... 
- 【JS新手教程】JS获取当前星期几的几种方法
		该文通过获取星期几的几种方法,介绍JS里的数组,判断,和字符串截取,可以当作新手教程看,小白也看的懂.获取星期几,可通过Date()对象的getDay()获取,获取的是一个数字,对应的是0,1,2,3 ... 
- 无限循环轮播图之JS部分(原生JS)
		JS逻辑与框架调用, <script type="text/javascript"> var oBox = document.getElementById('box') ... 
随机推荐
- 201521123014 《Java程序设计》第8周学习总结
			1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 泛型(编写的代码可被不同类型的对象所重用) Java中一个集合可以放任何类型的对象,因为任何对象都 is-a ... 
- 201521044091《Java程序设计》第7周学习总结
			1. 本周学习总结 ArrayList代码分析 1.1 解释ArrayList的contains源代码 用于判断Collection中是否包含某个元素.List<T>的contains方法 ... 
- 201521123099 《Java程序设计》第6周学习总结
			1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ... 
- 201521123118《java程序与设计》第4周作业总结
			1.本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点 1.2 使用常规方法总结其他上课内容. 为了不必要写重复的代码,可以运用继承,用关键字extends来定义一个类,被继承的类叫做父类,继 ... 
- 201521123024《Java程序设计》第3周学习总结
			1. 本周学习总结 2. 书面作业 1.代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; p ... 
- 201521123090《JAVA程序设计》第二周学习总结
			1. 本章学习总结 java基本数据类型 String类对象使用 枚举类型及switch分支 容器的概念 2. 书面作业 Q1.使用Eclipse关联jdk源代码(截图),并查看String对象的源代 ... 
- Python[小甲鱼007了不起的分支和循环]
			加载背景音乐播放背景音乐(设置单曲循环)我方飞机诞生interval = 0while True:if 用户是否点击关闭按钮退出程序breakinterval += 1if interval = 50 ... 
- 201521145048《Java程序设计》第11周学习总结
			1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 Q1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) ... 
- Java内部类的总结
			内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同. 内部类分为: 成员内部类.局部内部类.静态嵌套类.匿名内部类 . 1.成员内部类 成员内部类是最普通的内部类,它的定义为位于另一个类 ... 
- Linux第一篇【介绍、安装Ubuntu、基本目录结构】
			Linux介绍 Linux:不管是不是我们这些学编程的都肯定会听说过这么一个系统,一般地,我们在PC端都是用Windows操作系统,那我们学习Linux操作系统有什么用呢??? 由于我们的JAVAEE ... 
