js中简易选项卡的实现(3种方法实现样式的封装)

本文介绍了JavaScript中实现选项卡效果的三种方法,包括利用overflow: hidden;、clear: both; 和 :after伪元素清除浮动。文章强调了在处理点击事件时的异步性质和作用域问题,并提供了两种解决方案:使用自定义属性和闭包。同时,展示了如何将选项卡功能封装成一个插件,以适应多个选项卡场景。文中提供了相关HTML和JavaScript代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

清除浮动的三种方式

1.overflow: hidden; 清除子元素的浮动对父元素的影响
2.clear: both; 清除哥哥元素的浮动对弟弟元素的影响
3.:after{ display: block; content: “”; clear: both; }

基本样式代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>选项卡</title>
    <style type="text/css">
        * {
            margin: 0px;
            padding: 0px;
            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            font-size: 14px;
            -webkit-user-select: none;
        }

        ul, li {
            list-style: none;
        }

        .box {
            width: 500px;
            margin: 10px auto;
        }

        .box ul {
            /*overflow: hidden; !*清除子元素的浮动对父元素的影响*!*/
            position: relative;
            top: 1px;
        }

        .box ul:after {
            display: block;
            content: "";
            clear: both;
        }

        .box ul li {
            float: left;
            margin-right: 15px;
            width: 100px;
            height: 30px;
            border: 1px solid #337ab7;
            line-height: 30px;
            text-align: center;
            cursor: pointer;
        }

        .box ul li.select {
            background: #5bc0de;
            border-bottom-color: #5bc0de;
        }

        .box div {
            display: none;
            height: 150px;
            text-align: center;
            line-height: 150px;
            background: #5bc0de;
            border: 1px solid #9783b9;
            /*clear: both; 清除哥哥元素的浮动对弟弟元素的影响*!*/
        }

        .box div.select {
            display: block;
        }
    </style>
</head>
<div class="box" id="tabFir">
    <ul>
        <li class="select">页卡一</li>
        <li>页卡二</li>
        <li>页卡三</li>
    </ul>
    <div class="select">内容一</div>
    <div>内容二</div>
    <div>内容三</div>
</div>
<script type="text/javascript">

</script>
</body>
</html>

预览图:
这里写图片描述

js代码——点击事件

获取元素:

var tabFir = document.getElementById("tabFir"),
    oLis = tabFir.getElementsByTagName("li"),
    oDivs = tabFir.getElementsByTagName("div");

在操作点击事件时,容易把代码写成下面这种情况:

for (var i = 0; i < oLis.length; i++) {
    oLis[i].onclick = function () {
        changeTab(i);
    }
}

但是这样是不对的,原因有三点:
1、js中所有的事件绑定都是异步编程的,开始只是给元素的点击胸围绑定了一个方法,但是需要手动点击才会执行这个方法,在此期间,不会干等着,继续下一次循环,当点击的时候循环早已经结束
2、在给元素绑定事件的时候,绑定的这个方法还只是定义的部分,此时方法中存储的都是字符串,我们看到的i只是一个字符
3、当点击的时候,执行对应的绑定方法,形成一个私有的作用域,在A中会使用到变量i,而i不是自己私有的,是上级作用域window下的i,此时windoew下的i已经变为oLis.length
解决此问题可以有两种方式:使用自定义属性的方式和闭包
自定义属性方式代码:

for (var i = 0; i < oLis.length; i++) {
    oLis[i].index = i; //给对象增加一个属性名index
    oLis[i].onclick = function () {
        changeTab(this.index);
    }
}

闭包方式代码:

//第一种:
for (var i = 0; i < oLis.length; i++) {
    ~function (i) {
        oLis[i].onclick = function () {
            changeTab(i);
        }
    }(i);
}

//第二种:
for (var i = 0; i < oLis.length; i++) {
    oLis[i].onclick = (function (i) {
        return function () {
            changeTab(i);
        }
    })(i);
}

HTML+JavaScript代码如下,其中utils中方法见:http://blog.youkuaiyun.com/ruirui_1996/article/details/78116836

<div class="box" id="tabFir">
    <ul id="tabOptions">
        <li class="select">页卡一</li>
        <li>页卡二</li>
        <li>页卡三</li>
    </ul>
    <div class="select">
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
    </div>
    <div>内容二</div>
    <div>内容三</div>
</div>
<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript">
    var tabFir = document.getElementById("tabFir"),
        tabFirst = utils.firstChild(tabFir),  //ul
        oLis = utils.children(tabFirst);  //三个li
    for (var i = 0; i < oLis.length; i++) {
        oLis[i].onclick = function () {
            //首先把兄弟元素的选中样式都移除掉
            var curSiblings = utils.siblings(this); //this是当前事件,即当前绑定的li,siblings获取所有的兄弟元素节点
            for (var i = 0; i < curSiblings.length; i++) {
                utils.removeClass(curSiblings[i], "select"); //removeClass删除样式类名
            }
            //再让当前点击这个元素有选中的样式
            utils.addClass(this, "select"); //addClass:给元素增加样式类名
            //再让当前点击这个li父亲元素的所有的弟弟元素中(三个div)中和当前点击的这个li索引相同的有选中的样式,其余的移除选中样式
            var index = utils.index(this); //index:获取当前元素的索引
            var divList = utils.nextAll(this.parentNode); //nextAll:获取所有的弟弟元素节点
            for (var i = 0; i < divList.length; i++) {
                i === index ? utils.addClass(divList[i], "select") : utils.removeClass(divList[i], "select");
            }
        }
    }
</script>

预览图:
这里写图片描述

但是该段代码只适合一个选项卡的情况,如果选项卡较多的话,最好写成一个插件来封装
这里写图片描述

样式封装——一个一个绑定

单独建立一个“封装.js” 文件:

//实现一个选项卡的封装,只要多个选项卡的主体结构一样,那么每一个实现的思想都是一模一样的,唯一不一样的就是最外层的盒子不一样
~function () {
    /*
     * tabChange封装一个选项卡插件,只要大结构保持统一,即可实现选项卡功能
     * 参数:container:当前要实现选项卡的这个容器
     * defaultIndex:默认选中项的索引
     */
    function tabChange(container, defaultIndex) {
        var tabFirst = utils.firstChild(container),  //ul
            oLis = utils.children(tabFirst),  //三个li
            divList = utils.children(container, "div"); //得到div的元素标签
        //让defaultIndex对应的页卡选中的样式
        defaultIndex = defaultIndex || 0;
        utils.addClass(oLis[defaultIndex], "select");
        utils.addClass(divList[defaultIndex], "select");
        //实现具体的切换功能
        for (var i = 0; i < oLis.length; i++) {
            oLis[i].onclick = function () {
                var curSiblings = utils.siblings(this);
                for (var i = 0; i < curSiblings.length; i++) {
                    utils.removeClass(curSiblings[i], "select");
                }
                utils.addClass(this, "select");
                var index = utils.index(this);
                for (var i = 0; i < divList.length; i++) {
                    i === index ? utils.addClass(divList[i], "select") : utils.removeClass(divList[i], "select");
                }
            }
        }
    }

    //在闭包里面,如果想让外部使用,可以暴露一个接口selectTab
    window.selectTab = tabChange;
}();

html文件中的js代码为:

<script type="text/javascript" src="utils.js"></script>
<script type="text/javascript" src="封装.js"></script>
<script type="text/javascript">
    var boxList = utils.getElementsByClass("box"); //getElementsByClass:通过元素的样式类名获取一组元素集合(兼容所有浏览器)
    for (var i = 0; i < boxList.length; i++) {
        selectTab(boxList[i]);
    }
</script>

可实现选项卡效果。

样式封装——事件委托

“封装.js” 代码:

//实现一个选项卡的封装,只要多个选项卡的主体结构一样,那么每一个实现的思想都是一模一样的,唯一不一样的就是最外层的盒子不一样
~function () {
    /*
     * tabChange封装一个选项卡插件,只要大结构保持统一,即可实现选项卡功能
     * 参数:container:当前要实现选项卡的这个容器
     * defaultIndex:默认选中项的索引
     */
    function tabChange(container, defaultIndex) {
        var tabFirst = utils.firstChild(container),  //ul
            oLis = utils.children(tabFirst),  //三个li
            divList = utils.children(container, "div"); //得到div的元素标签
        //使用时间委托来优化点击操作
        tabFirst.onclick = function (e) {
            e = e || window.event;
            e.target = e.target || e.srcElement;
            if (e.target.tagName.toLowerCase() === "li") { //说明当前点击的是li标签
                detailFn.call(e.target, oLis, divList);
            }
        };
        //让defaultIndex对应的页卡选中的样式
        defaultIndex = defaultIndex || 0;
        utils.addClass(oLis[defaultIndex], "select");
        utils.addClass(divList[defaultIndex], "select");
    }
    function detailFn(oLis, divList) {
        //保证this是当前点击的li
        var index = utils.index(this);
        utils.addClass(this, "select");
        for (var i = 0; i < divList.length; i++) {
            i === index ? utils.addClass(divList[i], "select") : (utils.removeClass(divList[i], "select"), utils.removeClass(oLis[i], "select"));
        }
    }
    //在闭包里面,如果想让外部使用,可以暴露一个接口selectTab
    window.selectTab = tabChange;
}();

HTML中js代码:

<script type="text/javascript">
    var boxList = utils.getElementsByClass("box"); //getElementsByClass:通过元素的样式类名获取一组元素集合(兼容所有浏览器)
    for (var i = 0; i < boxList.length; i++) {
        selectTab(boxList[i]);
    }
</script>
样式封装——构造函数面向对象

“封装.js” 代码:

//实现一个选项卡的封装,只要多个选项卡的主体结构一样,那么每一个实现的思想都是一模一样的,唯一不一样的就是最外层的盒子不一样
~function () {
    function TabChange(container, defaultIndex) {
        return this.init(container, defaultIndex);
    }

    TabChange.prototype = {
        constructor: TabChange,
        //按照索引来设置默认选中的页卡
        defaultIndexEven: function () {
            utils.addClass(this.oLis[this.defaultIndex], "select");
            utils.addClass(this.divList[this.defaultIndex], "select");
        },
        //事件委托实现绑定切换
        liveClick: function () {
            var _this = this;
            this.tabFirst.onclick = function (e) {
                e = e || window.event;
                e.target = e.target || e.srcElement;
                if (e.target.tagName.toLowerCase() === "li") {
                    _this.detailFn(e.target);
                }
            };
        },
        detailFn: function (curEle) {
            var index = utils.index(curEle);
            utils.addClass(curEle, "select");
            for (var i = 0; i < this.divList.length; i++) {
                i === index ? utils.addClass(this.divList[i], "select") : (utils.removeClass(this.divList[i], "select"), utils.removeClass(this.oLis[i], "select"));
            }
        },
        //初始化效果,也是当前插件的唯一入口
        init: function (container, defaultIndex) {
            this.container = container || null;
            this.defaultIndex = defaultIndex || 0;
            this.tabFirst = utils.firstChild(this.container);
            this.oLis = utils.children(this.tabFirst);
            this.divList = utils.children(this.container, "div");
            this.defaultIndexEven();
            this.liveClick();
            return this;
        }
    };
    window.selectTab = TabChange;
}();

HTML中js代码:

<script type="text/javascript">
    var boxList = utils.getElementsByClass("box"); //getElementsByClass:通过元素的样式类名获取一组元素集合(兼容所有浏览器)
    var box1 = new selectTab(boxList[0],0);
    var box1 = new selectTab(boxList[1],1);
    var box1 = new selectTab(boxList[2],2);
</script>
封装一个选项卡,不过已经不像选项卡了-_-!!! 现稍微说明下吧,如果不明白的话,旁边有几个例子可能说明起来更清楚些 obj.bind = ["a1","td","b1","div"]; 绑定id="a1"下的td标签为菜单,绑定id="b1"下的div标签为内容,简单么? td标签的数量和div标签的数量必须相同 (若不需要显示内容,只显示菜单话,可以这个在td标签上加<td skip="true">) 如果id="a1"下的td标签有嵌套表格,这样的话,就不是所有的td都是菜单,这时候需要用下nesting obj.nesting = [false,true,"",""]; 当标签tag有嵌套时,需要用到这个 比如选项卡内容是放在div容器里,而本身这个内容里也有div标签,这时就需要用到 菜单嵌套为false,内容嵌套为true,且会自动判断出内容标签,多数时候这样就可以了 判断方法为,认定getElementsByTagName后第一个标签为内容第一项,其他的就用这个第一项的深度来判断 但有些情况下,这样还是不行 我用后面2个参数做id来指定菜单或者内容的第一项nesting = [false,true,"","q2"]; 这样就肯定不会错了(不明白的话看下例子就简单多了) obj.index = 0; 默认显示第几个选项卡,序号从0开始 obj.style = ["c1","c2","c3"] 菜单加载的样式的className: 菜单未选中的className是c1 菜单选中的className是c2 菜单onmouseover的className是c3 obj.overStyle = false; 选项卡是否有onmouseover, onmouseout变换样式事件[非激活选项卡内容],对应的样式为style[2] obj.overChange = false; 选项卡内容是否用onmouseover, onmouseout直接激活 obj.menu = false; 选项卡是菜单类型 obj.auto = [false, 1000]; 选项卡是否自动播放,播放速度(毫秒) obj.creat(); 开始生成选项卡,需要onclick触事件的话,可以obj.creat(函数名) 所有的都会触
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值