在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>> [js插件开发教程]原生js仿jquery架构扩展开发选项卡插件.
还缺少两个常用的切换(自动切换与透明度渐变),当然有朋友会说,还有左右,上下等等,这些动画会放在焦点图(幻灯片)插件系列.
(自动切换,停止控制,透明度渐变 ) 效果预览:
自动切换的实现:
这个思路很简单,开启定时器,让选项卡的索引+1,加到4的时候(选项卡的长度)从0开始
传统做法:
index = 0
index++
if ( index == 4 ) {
index = 0
}
小技巧(估计很多人都没有用过):
var i = ( index + 1 ) % 4
index为当前选中的选项卡 索引
当index = 0,他下一张就是1, 通过上面的取余操作,i = 1
当index = 3,他下一张就是0, 通过上面的取余操作,i = 0
这种方法不需要判断边界,只需要一句代码。在实际开发中,把那个4替换成选项卡的长度
好了,关键的思路和技巧有了,我们开始拼接框架了:
var defaults = {
contentClass : 'tab-content',
navClass : 'tab-nav',
activeClass : 'active',
triggerElements : '*',
activeIndex : 0,
evType : 'click',
effect : 'none',
auto : false,
delay : 3000,
duration : 1000
};
defaults参数,增加几个配置:
effect: none(没有特效) / fade( 透明度切换 )
auto: false(不会自动切换) / true ( 开启自动切换 )
delay : 多少时间 切换一个选项卡
duration: 透明度开启,这个才会用到,表示,多长时间内 完成透明度的切换
if ( options.effect == 'fade' ) {
tabContent.style.position = 'relative';
for( var i = 0; i < tabContentEle.length; i++ ) {
tabContentEle[i].style.position = 'absolute';
}
tabContentEle[opt.activeIndex].style.zIndex = _contentLen + 1;
opt.delay += opt.duration;
}
当开启透明度变化的时候,把选项卡元素设置成定位方式,当前选中的选项卡,层级为最高!!! ( 如果不是最高层级,那么默认是最后一个选项卡在最上面,所以 “内容4” 就会在最上层,显然不是我们想要的结果)层级+定位 这一招也很常用,经常用来做显示隐藏,和透明度变化.
根据opt配置,判断是否开启了auto自动切换功能
//是否自动播放
if ( opt.auto ) {
for( var i = 0 ; i < tabNavEle.length; i++ ){
tabNavEle[i].index = i;
tabNavEle[i].onmouseover = function(){
_api.stop();
_api.setIndex( this.index );
};
tabNavEle[i].onmouseout = function(){
_api.start();
_api.setIndex( this.index );
};
}
_api.start();
}
如果开启了,做两件事情:
1,调用start()函数,让索引+1
2,选项卡导航部分,添加事件控制 自动播放的暂停和开始
start与stop方法?
_api.stop = function(){
timer && clearInterval( timer );
}; _api.start = function(){
_api.stop();
timer = setInterval( function(){
_api.next();
}, opt.delay );
};
调用next方法,你应该猜得到next方法做的事情就是索引+1 吧
_api.next = function(){
var i = ( _index + 1 ) % _contentLen;
_api.setIndex( i );
};
索引+1之后,再切换选项卡
最后在setIndex方法:增加透明度变化
if ( _index != index ) {
tabContentEle[_index].style.zIndex = _contentLen + 1;
for (var i = 0; i < tabContentEle.length; i++) {
if (i != _index) {
tabContentEle[i].style.zIndex = ( index + _contentLen - ( i + 1 ) ) % _contentLen + 1;
tabContentEle[i].style.opacity = 1;
}
}
animate(tabContentEle[_index], {'opacity': 0}, function () {
tabContentEle[_index].style.zIndex = ( index + _contentLen - ( _index + 1 ) ) % _contentLen + 1;
_index = index;
});
}
完整的js代码有220行:
/**
* 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;
}; function css(obj, attr, value) {
if (arguments.length == 3) {
obj.style[attr] = value;
} else {
if (obj.currentStyle) {
return obj.currentStyle[attr];
} else {
return getComputedStyle(obj, false)[attr];
}
}
} function animate(obj, attr, fn) {
clearInterval(obj.timer);
var cur = 0;
var target = 0;
var speed = 0;
var start = new Date().getTime();//起始时间
obj.timer = setInterval(function () {
var bFlag = true;
for (var key in attr) {
if (key == 'opacity') {
cur = css(obj, 'opacity') * 100;
} else {
cur = parseInt(css(obj, key));
}
target = attr[key];
speed = ( target - cur ) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (cur != target) {
bFlag = false;
if (key == 'opacity') {
obj.style.opacity = ( cur + speed ) / 100;
obj.style.filter = "alpha(opacity:" + ( cur + speed ) + ")";
} else {
obj.style[key] = cur + speed + "px";
}
}
}
if (bFlag) {
var end = new Date().getTime();//结束时间
console.log( '总计:', ( end - start ) );
clearInterval(obj.timer);
fn && fn.call(obj);
}
}, 30 );
} G.fn.tabs = function( options ){
options = options || {};
var defaults = {
contentClass : 'tab-content',
navClass : 'tab-nav',
activeClass : 'active',
triggerElements : '*',
activeIndex : 0,
evType : 'click',
effect : 'none',
auto : false,
delay : 3000,
duration : 1000
}; 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 timer = null; if ( options.effect == 'fade' ) {
tabContent.style.position = 'relative';
for( var i = 0; i < tabContentEle.length; i++ ) {
tabContentEle[i].style.position = 'absolute';
}
tabContentEle[opt.activeIndex].style.zIndex = _contentLen + 1;
opt.delay += opt.duration;
} var _api = {}; _api.next = function(){
var i = ( _index + 1 ) % _contentLen;
_api.setIndex( i );
}; _api.stop = function(){
timer && clearInterval( timer );
}; _api.start = function(){
_api.stop();
timer = setInterval( function(){
_api.next();
}, opt.delay );
}; _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':
if ( _index != index ) {
tabContentEle[_index].style.zIndex = _contentLen + 1;
for (var i = 0; i < tabContentEle.length; i++) {
if (i != _index) {
tabContentEle[i].style.zIndex = ( index + _contentLen - ( i + 1 ) ) % _contentLen + 1;
tabContentEle[i].style.opacity = 1;
}
}
animate(tabContentEle[_index], {'opacity': 0}, function () {
tabContentEle[_index].style.zIndex = ( index + _contentLen - ( _index + 1 ) ) % _contentLen + 1;
_index = index;
});
}
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 );
} //是否自动播放
if ( opt.auto ) {
for( var i = 0 ; i < tabNavEle.length; i++ ){
tabNavEle[i].index = i;
tabNavEle[i].onmouseover = function(){
_api.stop();
_api.setIndex( this.index );
};
tabNavEle[i].onmouseout = function(){
_api.start();
_api.setIndex( this.index );
};
}
_api.start();
} return this;
} var $ = function( selectors, context ){
return G( selectors, context );
}
window.$ = $;
})();
[js插件开发教程]实现一个比较完整的开源级选项卡插件的更多相关文章
-
[js插件开发教程]定制一个手风琴插件(accordion)
本文带来一个垂直方向的手风琴插件开发,可以定制的功能如下: contentClass : 'panel', //面板样式navClass : 'nav', //导航样式activeClass : 'a ...
-
[js插件开发教程]一步步开发一个可以定制配置的隔行变色小插件
隔行变色功能,不用js,直接用css伪类就可以做,这个实例可以作为js插件开发很好的入门级实例.本文实现的隔行变色包括以下功能: 1,支持2种常用结构共存( div元素 和 表格类型 ) 2,一个页面 ...
-
[js插件开发教程]原生js仿jquery架构扩展开发选项卡插件
jquery插件一般是这么干的: $.fn.插件名称 = function(){}, 把插件的名称加在.fn上,在源码里面实际上是扩展到构造函数的原型对象上,如果你没看过jquery的源代码,或者你曾 ...
-
jQuery插件开发教程
jQuery插件开发教程 ——让你的jQuery水平提升一个台阶 要说jQuery 最成功的地方,我认为是它的可扩展性吸引了众多开发者为其开发插件,从而建立起了一个生态系统.这好比大公司们争相做平台 ...
-
(转)跟我一起学JQuery插件开发教程
在逛codeproject网站的时候,突然看到一篇文章:How to write plugin in Jquery. 如果对E文好的同学 ,可以看上面的连接.现在我把上面网站的及结合自己的想法写这篇文 ...
-
Js插件开发
简易JS插件开发,本文效果是一个简单的弹出层,意在记录插件的封装Demo. 完整源码压缩包:demo.rar 效果图(如下): 插件脚本: /** * 节点配置属性方式配置参数:专业的做法是配置到,每 ...
-
跟我一起学JQuery插件开发教程
在逛codeproject网站的时候,突然看到一篇文章:How to write plugin in Jquery. 如果对E文好的同学 ,可以看上面的连接.现在我把上面网站的及结合自己的想法写这篇文 ...
-
分享我对JS插件开发的一些感想和心得
本文阅读目录: •起因•如何开发一个轻量级的适用性强的插件•总结 起因 如果大家平时做过一些前端开发方面的工作,一定会有这样的体会:页面需要某种效果或者插件的时候,我们一般会有两种选择: 1.上网查找 ...
-
js插件开发的一些感想和心得
起因 如果大家平时做过一些前端开发方面的工作,一定会有这样的体会:页面需要某种效果或者插件的时候,我们一般会有两种选择:1.上网查找相关的JS插件,学习其用法2.自己造*,开发插件. 寻找存在的插件 ...
随机推荐
-
AVAssetReader+AVAssetReaderTrackOutput播放视频
该文章引用自:http://www.jianshu.com/p/3d5ccbde0de1 IOS 微信聊天发送小视频的秘密(AVAssetReader+AVAssetReaderTrackOutput ...
-
ASP.NET MVC进阶三
一.ASP.NET MVC中的AJAX应用 首先,在ASP.NET MVC中使用自带的ajax功能,必须要导入2个js文件(顺序不能颠倒): ASP.NET MVC提供了2个常用的ajax辅助方法. ...
-
POI教程之第一讲:创建新工作簿, Sheet 页,创建单元格
第一讲 Poi 简介 Apache POI 是Apache 软件基金会的开放源码函数库,Poi提供API给java程序对Microsoft Office格式档案读和写的功能. 1.创建新工作簿,并给工 ...
-
c#导出excel(转)
C#导出Excel文件实例代码 2010-08-03 14:10:36| 分类: 软件编程 | 标签:excel c#导出excel |字号大中小 订阅 /// <summary> ...
-
iOS在照片上添加水印
在做项目的时候我们需要将拍摄的照片做上标记防止图片被他人盗用,所以这就需要在照片的上面加上水印,以表示此照片的独一无二. 加水印不是要在上面添加上几个Label,而是我们要把字画到图片上成为一个整体. ...
-
洛谷mNOIP模拟赛Day1-斐波那契
题目背景 大样例下发链接:http://pan.baidu.com/s/1c0LbQ2 密码:jigg 题目描述 小 C 养了一些很可爱的兔子. 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家 ...
-
周一04.2流程控制if……else
语法一: if 条件1: 代码1 代码2 例题:如果年龄>20岁,那么:叫阿姨 age=22if age>20: print('阿姨') 语法二: if 条件1: 代码1 代码2else ...
-
包建强的培训课程(1):Android App企业级开发
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
-
spring遇到的Error applying BeanValidation relational constraints
spring3.1+hibernate4集成测试时遇到的问题: log4j:WARN No appenders could be found for logger (org.springframewo ...
-
一切皆Socket
“一切皆Socket!” 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. ——有感于实际编程和开源项目研究. socket()函数介绍 socket函数介绍 函数原型 domai ...