代码改变世界

一个javascript选项卡的进化史

2012-10-25 16:46  youxin  阅读(312)  评论(0)    收藏  举报

一、结构

使用合理的HTML结构是javascript UI组件优雅的最重要条件之一,这里使用dl、dt、dd构建选项卡结构(js罗浮宫讨论成果,见司徒正美:jquery tabs插件

 以下为引用的内容:

<dl class="artTabs">
<dt id="tabs"><a href="#tabContent1" class="select">link1</a> <a href="#tabContent2">link2</a> <a href="#tabContent3">link3</a></dt>
<dd id="tabContent1" style="display:block">tabContent1</dd>
<dd id="tabContent2">tabContent2</dd>
<dd id="tabContent3">tabContent3</dd>
</dl>

选项卡按钮的锚点都指向内容,这样在脚本与样式失效的情况下仍然能够进行基本的跳转。

二、编写核心javascript代码

接下来就是js编写。使用事件代理机制可以更加高效的处理业务,无需循环遍历操作去给每个按钮绑定事件,也能节省内存:

//// 参数:选项卡按钮外包裹元素, 按钮选中的样式, 初始化选中的按钮索引值(以0开始)
//在这个例子中就是tabs dt
var artTabs=function(bar,className,index)
{
    var gid=function(id){ return document.getElementById(id); };
    var buttons=bar.getElementsByTagName("a");
    var selectButton=buttons[index];
    var showContent=gid(selectButton.href.split('#')[1]);
    
    bar.onclick=function(event){
        event=event||window.event;
        var target=event.target||event.srcElement;
        
        if(target.nodeName.toLowerCase()==='a')
        {
            //更改display
            showContent.style.display='none';
            showContent=gid(target.href.split('#')[1]);
            showContent.style.display='block';
            //更改className;
            selectButton.className='';
            selectButton=target;
            target.className=className;
            return false;
        }
    };
    
};

window.onload=function()
{
    artTabs(document.getElementById("tabs"),'select',0);
}

还有结合css来开始来隐藏刚开始的元素显示:

.artTabs { background:#ffffff; }
.artTabs dt {
    padding:5px 5px 0 5px;
    background:#9FC;
    border-bottom:1px solid red;
}
.artTabs dt  a {
    display:inline-block;
}
.artTabs dt a.select{
    border-color:#F0F;
    color:green;
    background:#eee;
}
.artTabs dd{
    display:none; 最重要
    padding:10px;
    margin:0;
}

完整代码如下:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style>
.artTabs { background:#ffffff; }
.artTabs dt {
    padding:5px 5px 0 5px;
    background:#9FC;
    border-bottom:1px solid red;
}
.artTabs dt  a {
    display:inline-block;
}
.artTabs dt a.select{
    border-color:#F0F;
    color:green;
    background:#eee;
}
.artTabs dd{
    display:none;
    padding:10px;
    margin:0;
}


</style>
<script>
//// 参数:选项卡按钮外包裹元素, 按钮选中的样式, 初始化选中的按钮索引值(以0开始)
//在这个例子中就是tabs dt
var artTabs=function(bar,className,index)
{
    var gid=function(id){ return document.getElementById(id); };
    var buttons=bar.getElementsByTagName("a");
    var selectButton=buttons[index];
    var showContent=gid(selectButton.href.split('#')[1]);
    
    bar.onclick=function(event){
        event=event||window.event;
        var target=event.target||event.srcElement;
        
        if(target.nodeName.toLowerCase()==='a')
        {
            //更改display
            showContent.style.display='none';
            showContent=gid(target.href.split('#')[1]);
            showContent.style.display='block';
            //更改className;
            selectButton.className='';
            selectButton=target;
            target.className=className;
            return false;
        }
    };
    
};

window.onload=function()
{
    artTabs(document.getElementById("tabs"),'select',0);
}


</script>

</head>

<body>
<dl class="artTabs">
    <dt id="tabs">
   
       <a href="#tabContent1" class="select">Link1</a>
        <a href="#tabContent2"  >Link2</a>
        <a href="#tabContent3"  >Link3</a>
      </dt>
    
      <dd id="tabContent1" style="display:block;">tabContent1</dd>
      <dd id="tabContent2">tabContent2</dd>
      <dd id="tabContent3">tabContent3</dd>
      
      
</dl>
</body>
</html>

 

三、优化接口

是否发现这个刚出炉的选项卡参数有点麻烦?如果要支持鼠标靠近触发怎么办?或者点击选项卡的时候要采用ajax加载填充内容要如何处理回调函数?

显然我们前面的API设计并不能优雅的处理这些需求,我们改用字面量的方式传入参数,增加一些新接口,如:

 以下为引用的内容:

var artTabs=function(bar,config){
    var gid=function(id){ return document.getElementById(id); };
    
    config=config||{};
    var bar=typeof bar==='string'?gid(bar):bar,//从这里开始后面的定义都是变量,所有为逗号
    className = config.className || 'select',
    callback=config.callback||function(){ },
    isMouseover=config.isMouseover,
    
    buttons=bar.getElementsByTagName("a"),
    selectButton=buttons[
            config.index||
            function(){
                var ret=0;
                for(i=0;i<buttons.length;i++){
                    if(buttons[i].className===className) 
                        ret=i;
                }
                return ret;
            }()
         ],
        
    showContent=gid(selectButton.href.split('#')[1]),
    target,
    fn=function(event){
        event = event || window.event;
            target = event.target || event.srcElement;
            
            if (target.nodeName.toLowerCase() === 'a') {
                showContent.style.display = 'none';
                showContent = gid(target.href.split('#')[1]);
                showContent.style.display = 'block';
                selectButton.className = '';
                selectButton = target;
                target.className = className;
                target.focus();
                
                callback(selectButton,showContent);
                return false;
            }
    };
    
    if (isMouseover) bar.onmouseover = fn;
    bar.onclick = fn;// click事件至少能保证手持设备可以使用
};

注意var bar后面定义的都是一些变量,要以逗号结束。

我们可以这样调用:

artTabs('tabs');

或:

artTabs('tabs',{
    className:'select',
    isMouseover:true,
    callback:function(on,content){
        if(on.innerHTML==='Link3'){
            //ajax code [ajax取得数据后。。】
            content.innerHTML='我是动态写入的数据!';
        }
    }
});

 

 

四、扩展

原生代码执行效率往往会比框架高,当然我们还是可以很简单写几行代码为jQuery献身,成为其插件:

 

以下为引用的内容:

jQuery.fn.artTabs = function (config) {
return this.each(function () {
artTabs(this, config);
});
};

 调用范例:jQuery(‘.artTabs > dt’).artTabs();

查看演示:artTabs3.html

以上实现了选项卡最基本的功能,如果需要可以连选项卡HTML结构都封装进去、历史记录支持、URL记忆支持、Ajax数据加载支持等…

来自:http://www.kouok.net/html/1045.html