美文网首页JavaScript 进阶营
javascript:自定义选项卡插件

javascript:自定义选项卡插件

作者: Alisallon | 来源:发表于2017-12-15 12:39 被阅读0次

    对于经常开发前端的人,有很多喜欢直接用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

    相关文章

      网友评论

        本文标题:javascript:自定义选项卡插件

        本文链接:https://www.haomeiwen.com/subject/kuwlwxtx.html