1.实现三个标签的切换,不考虑那么多,只是实现点击menu1就显示content1;点击menu2显示content2;点击menu3显示content3。这个很容易实现:
<style type="text/css">
.tab{width:400px;text-align:left;margin:10px;}
.tab-menuWrapper{padding-left:20px;}
.tab-menuWrapper li{float:left;display:inline;border:1px solid #333;border-bottom:none;margin-right:5px;cursor:pointer;}
.tab-contnetWrapper{border:1px solid #333;clear:both;paddding:5px;}
</style>
<div class="tab">
<ul class="tab-menuWrapper">
<li id="tab-menu1">menu1</li>
<li id="tab-menu2">menu2</li>
<li id="tab-menu3">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div id="tab-content1">content1</div>
<div id="tab-content2" style="display:none;">content2</div>
<div id="tab-content3" style="display:none;">content3</div>
</div>
</div>
<script type="text/javascript">
var tabMenu1 = document.getElementById('tab-menu1'),
tabMenu2 = document.getElementById('tab-menu2'),
tabMenu3 = document.getElementById('tab-menu3'),
tabContent1 = document.getElementById('tab-content1'),
tabContent2 = document.getElementById('tab-content2'),
tabContent3 = document.getElementById('tab-content3');
tabMenu1.onclick = function(){
tabContent1.style.display="block";
tabContent2.style.display="none";
tabContent3.style.display="none";
}
tabMenu2.onclick = function(){
tabContent1.style.display="none";
tabContent2.style.display="block";
tabContent3.style.display="none";
}
tabMenu3.onclick = function(){
tabContent1.style.display="none";
tabContent2.style.display="none";
tabContent3.style.display="block";
}
</script>
2.这时增加需求,想把标签增加到5个或者增加到8个,依据上面的代码接下来就要复制代码改成相应的onclick事件,但是这样我们实现是在没完没了重复性的工作,想办法解决重复性工作的问题:
<div class="tab">
<ul class="tab-menuWrapper" id="tab-menuWrapper">
<li>menu1</li>
<li>menu2</li>
<li>menu3</li>
</ul>
<div class="tab-contnetWrapper" id="tab-contnetWrapper">
<div>content1</div>
<div style="display:none;">content2</div>
<div style="display:none;">content3</div>
</div>
</div>
<script type="text/javascript">
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = document.getElementById('tab-menuWrapper').getElementsByTagName('li'),
tabContents = document.getElementById('tab-contnetWrapper').getElementsByTagName('div');
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
tabMenus[i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[i].style.display = "block";
}
}
</script>
通过遍历标签数组,给每个标签添加click事件监听,比如:当点击第1个标签时,首先通过遍历把内容全部设成隐藏,然后再把相应的第1个内容显示。
预览发现浏览器报错:“tabContents[i] is undefined”
<script type="text/javascript">
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = document.getElementById('tab-menuWrapper').getElementsByTagName('li'),
tabContents = document.getElementById('tab-contnetWrapper').getElementsByTagName('div');
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
tabMenus[i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
alert(i)//3
tabContents[i].style.display = "block";
}
}
</script>
通过alert(i),发现点击时返回的始终是3,3,3并不是想要的0,1,2。这是为什么?
alert(i)只有在onclick事件发生时才会触发,当onclick触发时,弹出i的值,在函数内部并未定义变量i,这时去外层找,找到for循环里,这时i已经循环变成了3,所以alert(i)时会弹出3.
修复bug后的代码变成:
<script type="text/javascript">
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = document.getElementById('tab-menuWrapper').getElementsByTagName('li'),
tabContents = document.getElementById('tab-contnetWrapper').getElementsByTagName('div');
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
(function(_i){
tabMenus[_i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
//alert(_i)//3
tabContents[_i].style.display = "block";
}
})(i);
}
</script>
OK,现在可以了,这时无论我们添加几个标签切换,实现起来很容易了。
3.示例2中通过li,div标签来获取标签和内容组合,但是在实际工作中,往往标签切换的结构并不是这么简单,如果是一个复杂的标签切换的结构,再使用getElementsByTagName()方法就很悲剧了。
下面把html的结构变得复杂一些:
<div class="tab">
<ul class="tab-menuWrapper">
<li>menu1</li>
<li>menu2</li>
<li>menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div>
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div style="display:none;">
<div>content2</div>
<ul>
<li>3</li>
<li>4</li>
</ul>
</div>
<div style="display:none;">
<div>content3</div>
<ul>
<li>5</li>
<li>6</li>
</ul>
</div>
</div>
</div>
这时如果再使用示例2的脚本显然是不可以了,接下来修改脚本使它更通用。
通过给内容块添加样式名称用来用脚本选择。
<div class="tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
接下来的脚本自定义了getElementsByClassName()方法
通过getElementsByClassName的方法来选择内容块
<script type="text/javascript">
var GLOBAL = {};
GLOBAL.namespace = function(str){
var arr = str.split('.'),o = GLOBAL;
for(i = (arr[0] == GLOBAL)?1:0;i<arr.length;i++){
o[arr[i]] = o[arr[i]]||{};
o = o[arr[i]];
}
}
GLOBAL.namespace("Dom");
GLOBAL.Dom.getElementsByClassName = function(str,root,tag){
if(root){
root = (typeof root == "string")?document.getElementById(root):root;
}else{
root = document.body;
}
tag = tag||'*';
var els = root.getElementsByTagName(tag),arr = [];
for(var i=0,leng = els.length;i<leng;i++){
var k = els[i].className.split(' ');
for(j = 0,len = k.length;j<len;j++){
if(k[j] == str){
arr.push(els[i]);
break;
}
}
}
return arr;
}
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = GLOBAL.Dom.getElementsByClassName('J_tab-menu'),
tabContents = GLOBAL.Dom.getElementsByClassName('J_tab-content');
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
(function(_i){
tabMenus[_i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[_i].style.display = "block";
}
})(i);
}
</script>
这时就不用怕html结构复杂了吧。~~
4.但是问题又出现了,页面上有多个标签切换的模块,这时再使用示例3的脚本就会有问题了,这时可以考虑通过添加Id来控制选中的是哪个模块中的标签,但是这样,又会出现代码重复的问题:
<div class="tab" >>
脚本:
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = GLOBAL.Dom.getElementsByClassName('J_tab-menu','tab-Wrapper'),
tabContents = GLOBAL.Dom.getElementsByClassName('J_tab-content','tab-Wrapper');
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
(function(_i){
tabMenus[_i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[_i].style.display = "block";
}
})(i);
}
通过复制上面的脚本把相应的ID进行替换来实现。
显然这种方法违背了代码复用的原则,接下来看修改的代码:
<div class="tab J_tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
<div class="tab J_tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
<div class="tab J_tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
相应javascript代码,自定义getElementsByClassName()通过样式名称用来获取元素:
<script type="text/javascript">
var GLOBAL = {};
GLOBAL.namespace = function(str){
var arr = str.split('.'),o = GLOBAL;
for(i = (arr[0] == GLOBAL)?1:0;i<arr.length;i++){
o[arr[i]] = o[arr[i]]||{};
o = o[arr[i]];
}
}
GLOBAL.namespace("Dom");
GLOBAL.Dom.getElementsByClassName = function(str,root,tag){
if(root){
root = (typeof root == "string")?document.getElementById(root):root;
}else{
root = document.body;
}
tag = tag||'*';
var els = root.getElementsByTagName(tag),arr = [];
for(var i=0,leng = els.length;i<leng;i++){
var k = els[i].className.split(' ');
for(j = 0,len = k.length;j<len;j++){
if(k[j] == str){
arr.push(els[i]);
break;
}
}
}
return arr;
}
function setTab(root){
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = GLOBAL.Dom.getElementsByClassName('J_tab-menu',root),
tabContents = GLOBAL.Dom.getElementsByClassName('J_tab-content',root);
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
tabMenus[i]._index = i;
tabMenus[i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[this._index].style.display = "block";
}
}
}
var tabs = GLOBAL.Dom.getElementsByClassName('J_tab');
for(var i=0;i<tabs.length;i++){
setTab(tabs[i]);
}
</script>
4.接下来给当前选中的标签项加个高亮效果:
CSS:
<style type="text/css">
.tab{width:400px;text-align:left;margin:10px;}
.tab-menuWrapper{padding-left:20px;}
.tab-menuWrapper li{float:left;display:inline;border:1px solid #333;border-bottom:none;margin-right:5px;cursor:pointer;}
.tab-contnetWrapper{border:1px solid #333;clear:both;paddding:5px;}
.tab-currentMenu{background:#000;color:#fff;}
</style>
HTML:
<div class="tab J_tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu tab-currentMenu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
<div class="tab J_tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu tab-currentMenu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
<div class="tab J_tab">
<ul class="tab-menuWrapper">
<li class="J_tab-menu tab-currentMenu">menu1</li>
<li class="J_tab-menu">menu2</li>
<li class="J_tab-menu">menu3</li>
</ul>
<div class="tab-contnetWrapper">
<div class="J_tab-content">
<div>content1</div>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
<div class="J_tab-content" style="display:none;">
<p>content2</p>
<div>abc</div>
</div>
<div class="J_tab-content" style="display:none;">
content3
</div>
</div>
</div>
Javascript:
<script type="text/javascript">
var GLOBAL = {};
GLOBAL.namespace = function(str){
var arr = str.split('.'),o = GLOBAL;
for(i = (arr[0] == GLOBAL)?1:0;i<arr.length;i++){
o[arr[i]] = o[arr[i]]||{};
o = o[arr[i]];
}
}
GLOBAL.namespace("Dom");
GLOBAL.Dom.getElementsByClassName = function(str,root,tag){
if(root){
root = (typeof root == "string")?document.getElementById(root):root;
}else{
root = document.body;
}
tag = tag||'*';
var els = root.getElementsByTagName(tag),arr = [];
for(var i=0,leng = els.length;i<leng;i++){
var k = els[i].className.split(' ');
for(j = 0,len = k.length;j<len;j++){
if(k[j] == str){
arr.push(els[i]);
break;
}
}
}
return arr;
}
function setTab(root){
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = GLOBAL.Dom.getElementsByClassName('J_tab-menu',root),
tabContents = GLOBAL.Dom.getElementsByClassName('J_tab-content',root);
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
tabMenus[i]._index = i;
tabMenus[i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[this._index].style.display = "block";
var currentMenu = GLOBAL.Dom.getElementsByClassName('tab-currentMenu',root)[0];
if(currentMenu){
currentMenu.className = "";
}
this.className = "tab-currentMenu";
}
}
}
var tabs = GLOBAL.Dom.getElementsByClassName('J_tab');
for(var i=0;i<tabs.length;i++){
setTab(tabs[i]);
}
</script>
效果:
5.示例4里边的效果发现三个切换模块的高亮效果是相同的,但在实际的应用中也会出现高亮不同的情况,这个通过传参的形式来解决这个问题,首先新增两个样式:
<style type="text/css">
.tab{width:400px;text-align:left;margin:10px;}
.tab-menuWrapper{padding-left:20px;}
.tab-menuWrapper li{float:left;display:inline;border:1px solid #333;border-bottom:none;margin-right:5px;cursor:pointer;}
.tab-contnetWrapper{border:1px solid #333;clear:both;paddding:5px;}
.tab-currentMenu1{background:#000;color:#fff;}
.tab-currentMenu2{background:#f00;color:#fff;}
.tab-currentMenu3{background:#00f;color:#fff;}
</style>
接着对javascript进行修改:
<script type="text/javascript">
var GLOBAL = {};
GLOBAL.namespace = function(str){
var arr = str.split('.'),o = GLOBAL;
for(i = (arr[0] == GLOBAL)?1:0;i<arr.length;i++){
o[arr[i]] = o[arr[i]]||{};
o = o[arr[i]];
}
}
GLOBAL.namespace("Dom");
GLOBAL.Dom.getElementsByClassName = function(str,root,tag){
if(root){
root = (typeof root == "string")?document.getElementById(root):root;
}else{
root = document.body;
}
tag = tag||'*';
var els = root.getElementsByTagName(tag),arr = [];
for(var i=0,leng = els.length;i<leng;i++){
var k = els[i].className.split(' ');
for(j = 0,len = k.length;j<len;j++){
if(k[j] == str){
arr.push(els[i]);
break;
}
}
}
return arr;
}
function setTab(root,currentClass){
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = GLOBAL.Dom.getElementsByClassName('J_tab-menu',root),
tabContents = GLOBAL.Dom.getElementsByClassName('J_tab-content',root);
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
tabMenus[i]._index = i;
tabMenus[i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[this._index].style.display = "block";
var currentMenu = GLOBAL.Dom.getElementsByClassName(currentClass,root)[0];
if(currentMenu){
currentMenu.className = "";
}
this.className = currentClass;
}
}
}
var tabs = GLOBAL.Dom.getElementsByClassName('J_tab');
setTab(tabs[0],"tab-currentMenu1");
setTab(tabs[1],"tab-currentMenu2");
setTab(tabs[2],"tab-currentMenu3");
如果一个函数内某个因素很不稳定,我们可以将它从函数内部分离出来,以参数形式传入,从而将不稳定因素和函数解耦。
现在的效果:
嗯,不错,呵呵。
通过检测className是否等于tab-currentMenu1,如果是,就把样式名称替换成“”,并且把当前点击标签的样式名称替换成tab-currentMenu1,但是这样操作存在一个问题,就是会把和脚本操作无关的样式名也进行了修改。
如:
开始的代码:
操作之后发现样式名称被替换成了"":
接下来通过自定义addClass及removeClass来解决这个bug,修改后的代码:
<script type="text/javascript">
var GLOBAL = {};
GLOBAL.namespace = function(str){
var arr = str.split('.'),o = GLOBAL;
for(i = (arr[0] == GLOBAL)?1:0;i<arr.length;i++){
o[arr[i]] = o[arr[i]]||{};
o = o[arr[i]];
}
}
GLOBAL.namespace("Dom");
GLOBAL.Dom.getElementsByClassName = function(str,root,tag){
if(root){
root = (typeof root == "string")?document.getElementById(root):root;
}else{
root = document.body;
}
tag = tag||'*';
var els = root.getElementsByTagName(tag),arr = [];
for(var i=0,leng = els.length;i<leng;i++){
var k = els[i].className.split(' ');
for(j = 0,len = k.length;j<len;j++){
if(k[j] == str){
arr.push(els[i]);
break;
}
}
}
return arr;
}
GLOBAL.Dom.addClass = function(node,str){
var reg = new RegExp("(^|\\s+)"+str);
if(!reg.test(node.className)){
node.className = node.className+" "+str;
}
}
GLOBAL.Dom.removeClass = function(node,str){
var reg = new RegExp("(^|\\s+)"+str);
if(reg.test(node.className)){
node.className = node.className.replace(reg,"");
}
}
function setTab(root,currentClass){
//取得标签及内容的节点,并以数组的形式保存在变量中
var tabMenus = GLOBAL.Dom.getElementsByClassName('J_tab-menu',root),
tabContents = GLOBAL.Dom.getElementsByClassName('J_tab-content',root);
//遍历数组,让标签监听click事件
for(var i=0,leng = tabMenus.length;i<leng;i++){
tabMenus[i]._index = i;
tabMenus[i].onclick = function(){
for(var j=0,len = tabContents.length;j<len;j++){
tabContents[j].style.display="none";
}
tabContents[this._index].style.display = "block";
var currentMenu = GLOBAL.Dom.getElementsByClassName(currentClass,root)[0];
if(currentMenu){
GLOBAL.Dom.removeClass(currentMenu,currentClass);
}
GLOBAL.Dom.addClass(this,currentClass);
}
}
}
var tabs = GLOBAL.Dom.getElementsByClassName('J_tab');
setTab(tabs[0],"tab-currentMenu1");
setTab(tabs[1],"tab-currentMenu2");
setTab(tabs[2],"tab-currentMenu3");
</script>