对于经常开发前端的人,有很多喜欢直接用jQuery及jQuery插件,也有很多人喜欢自己写插件。相对于jQuery及jQuery插件,自己写的插件一般都很简单,易懂,也容易维护,也不臃肿。因最近夜里时间比较宽裕,所以准备分享一些自己写插件的过程和最终成果。
自定义选项卡插件
一、新建测试用的demo.html
1、添加3个<li>标签做选项卡用
<ul>
<!--选项卡1-->
<li>button 1</li>
<!--选项卡2-->
<li>button 2</li>
<!--选项卡3-->
<li>button 3</li>
</ul>
2、添加3个<div>做选项卡的分页
选项卡在切换的时候,将会显示对应的分页。
为了区分和看的明显,给不同的div设置不同的背景颜色和设置一个高度。
<!--选项卡分页1-->
<div id="tab1" style="background-color: antiquewhite; height: 100px;">
this is tab1.
</div>
<!--选项卡分页2-->
<div id="tab2" style="background-color: aqua; height: 100px;">
this is tab1.
</div>
<!--选项卡分页3-->
<div id="tab3" style="background-color: aquamarine; height: 100px;">
this is tab1.
</div>
效果如下:
image.png
二、设置<li>标签横排显示
1、给3个<li>标签设置style:
<ul>
<li style="float: left;">button 1</li>
<li style="float: left;">button 2</li>
<li style="float: left;">button 3</li>
</ul>
效果如下:
image.png
2、消除<li>的float对<div>的影响:
发现tab1分页显示有问题,此时需要给<div>设置一个style:clear:left,消除
<li>的float对它的影响:
......
<style>
.tabNode{
clear: both;
}
</style>
.....
<div id="tab1" class="tabNode" style="background-color: antiquewhite; height: 100px;">
this is tab 1.
</div>
<div id="tab2" class="tabNode" style="background-color: aqua; height: 100px;">
this is tab 2.
</div>
<div id="tab3" class="tabNode" style="background-color: aquamarine; height: 100px;">
this is tab 3.
</div>
效果如下:
image.png
二、美化<li>标签
我们发现<li>标签上带着小黑点,而且三个标签是连在一起的,为了好看,我们需要美化一下<li>标签。为了使三个标签通用一个style,我做了以下定义和设置:
......
<style>
.rootTab > li {
/*清理左侧的小黑点*/
list-style: none;
/*使后面的标签和当前标签横排显示*/
float: left;
margin: 1px;
padding: 15px;
/*添加边框*/
border: black solid 1px;
/*添加圆角*/
border-radius: 2px;
background-color: burlywood;
}
.tabNode{
clear: both;
}
</style>
......
<ul class="rootTab">
<li>button 1</li>
<li>button 2</li>
<li>button 3</li>
</ul>
......
效果如下(好看多了吧 ^_^ ):
image.png
三、写js,控制<li>标签的切换
在</body>和</html>标签中间添加<script></script>,准备在这里直接写js代码:
......
</body>
<script>
// TODO 准备在这里写
</script>
</html>
1、找到<li>标签
为了控制<li>标签,我们就要先找到<li>标签。因为<li>标签的父标签<ul>已经有一个class,所以我们可以通过该class找到<ul>标签,再通过<ul>标签的子节点找到其拥有的这些<li>标签:
<script>
// TODO 准备在这里写
var rootTabNodeList = document.querySelectorAll("ul.rootTab");
// 此时rootTabNodeList是个NodeList,这个里面存放的是拥有rootTab class的所有ul节点
// 下面准备遍历该rootTabNodeList,一个一个处理拥有rootTab class的ul节点
rootTabNodeList.forEach(function (rootTabNode) {
// 继续寻找该ul节点拥有的所有li节点
var childLiNodeList = rootTabNode.querySelectorAll("li");
// 此时childLiNodeList是个NodeList,这个里面存放的是rootTabNode拥有的所有li节点
});
</script>
2、给<li>标签添加点击事件
继续遍历childLiNodeList,给每一个<li>节点添加onclick事件:
<script>
// TODO 准备在这里写
......
// 此时childLiNodeList是个NodeList,这个里面存放的是rootTabNode拥有的所有li节点
// 继续遍历childLiNodeList,给每一个<li>节点添加onclick事件
childLiNodeList.forEach(function (childLiNode) {
childLiNode.onclick = function () {
alert("点击了我");
};
});
......
</script>
测试查看效果,包您满意 ^_^
3、把<div>分页和<li>标签关联起来
到目前为止,<div>分页和<li>标签还没产生任何联系,为了使<li>标签能够控制<div>分页,我给<li>标签添加了一个自定义的data属性,该属性的值,即对应<div>分页的id:
<ul class="rootTab">
<!--对应tab1 div分页-->
<li data-tab-node="tab1">button 1</li>
<!--对应tab2 div分页-->
<li data-tab-node="tab2">button 2</li>
<!--对应tab3 div分页-->
<li data-tab-node="tab3">button 3</li>
</ul>
这样,我们可以在js中通过获取<li>标签"data-tab"属性值,来获得对应的分页,并控制<div>分页的显示和隐藏。
4、获得<li>标签对应的<div>分页
<script>
// TODO 准备在这里写
......
// 继续遍历childLiNodeList,给每一个<li>节点添加onclick事件
childLiNodeList.forEach(function (childLiNode) {
// 获得<li>节点的data-tab-node属性
var tabNodeId = childLiNode.getAttribute("data-tab-node");
// 获得该tabNodeId对应的<div>分页节点
var tabNode = document.getElementById(tabNodeId);
childLiNode.onclick = function () {
};
});
......
</script>
5、控制<li>标签对应的<div>分页
现在打开页面,可以看到,3个<div>分页都是显示的,所以我们需要先把这三个<div>分页隐藏,并且在点击
<li>节点时,显示对应的<div>分页。
<script>
// TODO 准备在这里写
......
rootTabNodeList.forEach(function (rootTabNode) {
// 保存当前rootTab拥有的所有<li>节点
var tabButtonList = [];
// 保存当前rootTab拥有的所有<div>分页节点
var tabNodeList = [];
......
childLiNodeList.forEach(function (childLiNode) {
// 保存当前<li>节点到tabButtonList
tabButtonList.push(childLiNode);
......
// 保存当前<div>分页到tabNodeList
tabNodeList.push(tabNode);
childLiNode.onclick = function () {
// 隐藏所有<div>分页
hidenAllTabNode();
// 显示当前对应的<div>分页
tabNode.style.display = "block";
};
});
// 隐藏所有<div>分页
function hidenAllTabNode() {
tabNodeList.forEach(function (t) {
t.style.display = "none";
})
}
});
</script>
效果如下(点击tab button查看):
image.png
image.png
image.png
6、美化<li>标签效果
上面效果中,<li>标签点击时没什么变化,我们需要做下美化和脚本控制,增加鼠标滑过,鼠标点击等样式:
<style>
......
.rootTab > li:hover {
background-color: #de6090;
}
.rootTab > li.active,
.rootTab > li:active {
background-color: #de282e;
}
......
</style>
......
<script>
// TODO 准备在这里写
......
childLiNode.onclick = function () {
// 隐藏所有<div>分页
hidenAllTabNode();
// 显示当前对应的<div>分页
tabNode.style.display = "block";
// 清除所有<li>节点的激活状态
clearActiveForAllTabButton();
// 给当前<li>节点的激活状态
if (childLiNode.className.indexOf(" active") < 0) {
childLiNode.className += " active";
}
};
......
// 隐藏所有<div>分页
function hidenAllTabNode() {
tabNodeList.forEach(function (t) {
t.style.display = "none";
})
}
// 清除所有<li>节点的激活状态
function clearActiveForAllTabButton() {
tabButtonList.forEach(function (t) {
t.className = t.className.replace(" active", "");
})
}
});
</script>
效果如下:
鼠标点击后
image.png
鼠标悬浮时
image.png
7、初始化选项卡显示
在刚打开网页的时候,我们发现3个<div>分页还都一起显示,跟我们预期不一样,所以需要在网页打开时,在js里做一些初始化的操作,使页面刚打开时,默认选中选项卡1,显示<div>分页1。
<script>
// TODO 准备在这里写
......
rootTabNodeList.forEach(function (rootTabNode) {
......
// 显示指定index选项卡
function show(index) {
if(index < 0 || index >= tabNodeList.length){
index = 0;
}
// 隐藏所有<div>分页
hidenAllTabNode();
// 显示指定index的<div>分页
tabNodeList[index].style.display = "block";
// 清除所有<li>节点的激活状态
clearActiveForAllTabButton();
// 激活指定index的<li>节点
if (tabButtonList[index].className.indexOf(" active") < 0) {
tabButtonList[index].className += " active";
}
}
// 默认显示第2个选项卡
show(1);
});
</script>
效果如下:
image.png
四、封装插件
上面几步,我们把想要的基本功能实现了,这一步就是要把上面的实现逻辑封装一下,封装好的js,可以被更好的移植和使用。
1、创建一个TabBar对象
封装时需要用到闭包,我们先创建一个Tab对象:
var TabBar = (function () {
function TabBar() {
}
return TabBar;
}());
2、复制基本功能逻辑到TabBar的构造方法中
<script>
// TODO 准备在这里写
var TabBar = (function () {
function TabBar() {
var rootTabNodeList = document.querySelectorAll("ul.rootTab");
// 此时rootTabNodeList是个NodeList,这个里面存放的是拥有rootTab class的所有ul节点
// 下面准备循环遍历该rootTabNodeList,一个一个处理拥有rootTab class的ul节点
rootTabNodeList.forEach(function (rootTabNode) {
// 保存当前rootTab拥有的所有<li>节点
var tabButtonList = [];
// 保存当前rootTab拥有的所有<div>分页节点
var tabNodeList = [];
// 继续寻找该ul节点拥有的所有li节点
var childLiNodeList = rootTabNode.querySelectorAll("li");
// 此时childLiNodeList是个NodeList,这个里面存放的是rootTabNode拥有的所有li节点
// 继续遍历childLiNodeList,给每一个<li>节点添加onclick事件
childLiNodeList.forEach(function (childLiNode) {
// 保存当前<li>节点到tabButtonList
tabButtonList.push(childLiNode);
// 获得<li>节点的data-tab-node属性
var tabNodeId = childLiNode.getAttribute("data-tab-node");
// 获得该tabNodeId对应的<div>分页节点
var tabNode = document.getElementById(tabNodeId);
// 保存当前<div>分页到tabNodeList
tabNodeList.push(tabNode);
childLiNode.onclick = function () {
// 隐藏所有<div>分页
hidenAllTabNode();
// 显示当前对应的<div>分页
tabNode.style.display = "block";
// 清除所有<li>节点的激活状态
clearActiveForAllTabButton();
// 给当前<li>节点的激活状态
if (childLiNode.className.indexOf(" active") < 0) {
childLiNode.className += " active";
}
};
});
// 隐藏所有<div>分页
function hidenAllTabNode() {
tabNodeList.forEach(function (t) {
t.style.display = "none";
})
}
// 清除所有<li>节点的激活状态
function clearActiveForAllTabButton() {
tabButtonList.forEach(function (t) {
t.className = t.className.replace(" active", "");
})
}
// 显示指定index选项卡
function show(index) {
if(index < 0 || index >= tabNodeList.length){
index = 0;
}
// 隐藏所有<div>分页
hidenAllTabNode();
// 显示指定index的<div>分页
tabNodeList[index].style.display = "block";
// 清除所有<li>节点的激活状态
clearActiveForAllTabButton();
// 激活指定index的<li>节点
if (tabButtonList[index].className.indexOf(" active") < 0) {
tabButtonList[index].className += " active";
}
}
// 默认显示第2个
show(1);
});
}
return TabBar;
}());
// 初始化TabBar
new TabBar();
</script>
记得初始化TabBar
new TabBar();
运行查看效果,应该和第三步的效果一样。
3、给TabBar添加show方法,方便在外面控制选项卡的显示
添加show方法:
<script>
// TODO 准备在这里写
var TabBar = (function () {
function TabBar() {}
.......
TabBar.prototype = {
// 控制选项卡显示
// rootTabId:选项卡的id,即<ul>标签的id
// index:显示指定id的选项卡中的哪一个分页
show:function (rootTabId, index) {
}
};
return TabBar;
}());
.......
</script>
实现show方法,优化脚本,全部脚本如下:
<script>
// TODO 准备在这里写
var TabBar = (function () {
// 保存选项卡id和选项卡拥有的选项卡按钮的映射关系
tabButtonMap = [];
// 保存选项卡id和选项卡拥有的分页的映射关系
tabNodeMap = [];
function TabBar() {
var rootTabNodeList = document.querySelectorAll("ul.rootTab");
// 此时rootTabNodeList是个NodeList,这个里面存放的是拥有rootTab class的所有ul节点
// 下面准备循环遍历该rootTabNodeList,一个一个处理拥有rootTab class的ul节点
rootTabNodeList.forEach(function (rootTabNode) {
// 保存当前rootTab拥有的所有<li>节点
var tabButtonList = [];
// 保存当前rootTab拥有的所有<div>分页节点
var tabNodeList = [];
// 继续寻找该ul节点拥有的所有li节点
var childLiNodeList = rootTabNode.querySelectorAll("li");
// 此时childLiNodeList是个NodeList,这个里面存放的是rootTabNode拥有的所有li节点
// 继续遍历childLiNodeList,给每一个<li>节点添加onclick事件
childLiNodeList.forEach(function (childLiNode, index) {
// 保存当前<li>节点到tabButtonList
tabButtonList.push(childLiNode);
// 获得<li>节点的data-tab-node属性
var tabNodeId = childLiNode.getAttribute("data-tab-node");
// 获得该tabNodeId对应的<div>分页节点
var tabNode = document.getElementById(tabNodeId);
// 保存当前<div>分页到tabNodeList
tabNodeList.push(tabNode);
childLiNode.onclick = function () {
// 显示对应index的<div>分页
show(tabButtonList, tabNodeList, index);
};
});
// 添加该选项卡id和该选项卡拥有的选项卡按钮列表到tabButtonMap
tabButtonMap[rootTabNode.id] = tabButtonList;
// 添加该选项卡id和选项卡拥有的分页列表到tabNodeMap
tabNodeMap[rootTabNode.id] = tabNodeList;
// 默认显示第1个
show(tabButtonList, tabNodeList, 0);
});
}
// 显示指定index选项卡
function show(tabButtonList, tabNodeList, index) {
if(tabButtonList === null || tabButtonList === undefined){
return;
}
if(tabButtonList.length < 0){
return;
}
if(tabNodeList === null || tabNodeList === undefined){
return;
}
if(tabNodeList.length < 0){
return;
}
if (index < 0 || index >= tabNodeList.length) {
index = 0;
}
// 隐藏所有<div>分页
tabNodeList.forEach(function (t) {
t.style.display = "none";
});
// 显示指定index的<div>分页
tabNodeList[index].style.display = "block";
// 清除所有<li>节点的激活状态
tabButtonList.forEach(function (t) {
t.className = t.className.replace(" active", "");
});
// 激活指定index的<li>节点
if (tabButtonList[index].className.indexOf(" active") < 0) {
tabButtonList[index].className += " active";
}
}
TabBar.prototype = {
// 控制选项卡显示
// rootTabId:选项卡的id,即<ul>标签的id
// index:显示指定id的选项卡中的哪一个分页
show:function (rootTabId, index) {
show(tabButtonMap[rootTabId], tabNodeMap[rootTabId], index);
}
};
return TabBar;
}());
</script>
给<ul>标签设置id:tabBar1
<ul id="tabBar1" class="rootTab">
在脚本中调用:
new TabBar();
或
new TabBar().show("tabBar1", 1);
运行查看效果是否是预期的效果。
4、设置TabBar对象为单例
因为在脚本中,我们处理的是所有class包含rootTab的元素,所以当页面中有其他的class包含rootTab选项卡时,也会被该脚本处理。为了减少部分开销,不每次都new TabBar(),就需要把TabBar对象设置为单例。
在html中添加另一个选项卡,最终成果(html+css+js)如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>选项框</title>
</head>
<style>
.rootTab > li {
/*清理左侧的小黑点*/
list-style: none;
/*使后面的标签和当前标签横排显示*/
float: left;
margin: 1px;
padding: 15px;
/*添加边框*/
border: black solid 1px;
/*添加圆角*/
border-radius: 2px;
background-color: burlywood;
/*禁止鼠标双击选择文字*/
user-select: none;
/*添加手指鼠标显示*/
cursor: pointer;
}
.rootTab > li:hover {
background-color: #de6090;
}
.rootTab > li.active,
.rootTab > li:active {
background-color: #de282e;
}
.tabNode {
clear: both;
}
</style>
<body>
<ul id="tabBar1" class="rootTab">
<!--对应tab1 div分页-->
<li data-tab-node="tab1">button 1</li>
<!--对应tab2 div分页-->
<li data-tab-node="tab2">button 2</li>
<!--对应tab3 div分页-->
<li data-tab-node="tab3">button 3</li>
</ul>
<div id="tab1" class="tabNode" style="clear:left; background-color: antiquewhite; height: 100px;">
this is tab 1.
</div>
<div id="tab2" class="tabNode" style="background-color: aqua; height: 100px;">
this is tab 2.
</div>
<div id="tab3" class="tabNode" style="background-color: aquamarine; height: 100px;">
this is tab 3.
</div>
<ul id="tabBar2" class="rootTab">
<!--对应tab1 div分页-->
<li data-tab-node="tab4">button 4</li>
<!--对应tab2 div分页-->
<li data-tab-node="tab5">button 5</li>
<!--对应tab3 div分页-->
<li data-tab-node="tab6">button 6</li>
</ul>
<div id="tab4" class="tabNode" style="background-color: antiquewhite; height: 100px;">
this is tab 4.
</div>
<div id="tab5" class="tabNode" style="background-color: aqua; height: 100px;">
this is tab 5.
</div>
<div id="tab6" class="tabNode" style="background-color: aquamarine; height: 100px;">
this is tab 6.
</div>
</body>
<script>
// TODO 准备在这里写
var TabBar = (function () {
// 保存单例对象
var instance = null;
function TabBar() {
if (instance === null) {
instance = new TabBarObj();
}
return instance;
}
// 创建内部TabBar类
var TabBarObj = (function () {
// 保存选项卡id和选项卡拥有的选项卡按钮的映射关系
tabButtonMap = [];
// 保存选项卡id和选项卡拥有的分页的映射关系
tabNodeMap = [];
function TabBarObj() {
var rootTabNodeList = document.querySelectorAll("ul.rootTab");
// 此时rootTabNodeList是个NodeList,这个里面存放的是拥有rootTab class的所有ul节点
// 下面准备循环遍历该rootTabNodeList,一个一个处理拥有rootTab class的ul节点
rootTabNodeList.forEach(function (rootTabNode) {
// 保存当前rootTab拥有的所有<li>节点
var tabButtonList = [];
// 保存当前rootTab拥有的所有<div>分页节点
var tabNodeList = [];
// 继续寻找该ul节点拥有的所有li节点
var childLiNodeList = rootTabNode.querySelectorAll("li");
// 此时childLiNodeList是个NodeList,这个里面存放的是rootTabNode拥有的所有li节点
// 继续遍历childLiNodeList,给每一个<li>节点添加onclick事件
childLiNodeList.forEach(function (childLiNode, index) {
// 保存当前<li>节点到tabButtonList
tabButtonList.push(childLiNode);
// 获得<li>节点的data-tab-node属性
var tabNodeId = childLiNode.getAttribute("data-tab-node");
// 获得该tabNodeId对应的<div>分页节点
var tabNode = document.getElementById(tabNodeId);
// 保存当前<div>分页到tabNodeList
tabNodeList.push(tabNode);
childLiNode.onclick = function () {
// 显示对应index的<div>分页
show(tabButtonList, tabNodeList, index);
};
});
// 添加该选项卡id和该选项卡拥有的选项卡按钮列表到tabButtonMap
tabButtonMap[rootTabNode.id] = tabButtonList;
// 添加该选项卡id和选项卡拥有的分页列表到tabNodeMap
tabNodeMap[rootTabNode.id] = tabNodeList;
// 默认显示第1个
show(tabButtonList, tabNodeList, 0);
});
}
// 显示指定index选项卡
function show(tabButtonList, tabNodeList, index) {
if (tabButtonList === null || tabButtonList === undefined) {
return;
}
if (tabButtonList.length < 0) {
return;
}
if (tabNodeList === null || tabNodeList === undefined) {
return;
}
if (tabNodeList.length < 0) {
return;
}
if (index < 0 || index >= tabNodeList.length) {
index = 0;
}
// 隐藏所有<div>分页
tabNodeList.forEach(function (t) {
t.style.display = "none";
});
// 显示指定index的<div>分页
tabNodeList[index].style.display = "block";
// 清除所有<li>节点的激活状态
tabButtonList.forEach(function (t) {
t.className = t.className.replace(" active", "");
});
// 激活指定index的<li>节点
if (tabButtonList[index].className.indexOf(" active") < 0) {
tabButtonList[index].className += " active";
}
}
TabBarObj.prototype = {
// 控制选项卡显示
// rootTabId:选项卡的id,即<ul>标签的id
// index:显示指定id的选项卡中的哪一个分页
show: function (rootTabId, index) {
show(tabButtonMap[rootTabId], tabNodeMap[rootTabId], index);
return this;
}
};
return TabBarObj;
}());
return TabBar;
}());
new TabBar().show("tabBar1", 1).show("tabBar2", 0);
</script>
</html>
注意在脚本中调用:
new TabBar().show("tabBar1", 1).show("tabBar2", 0);
运行查看效果,如果与预期的一致,恭喜你,大功告成 ^_^ 。
image.png
网友评论