完整精准版选项卡案例(37)

该博客介绍了如何使用HTML、CSS和JavaScript实现一个简单的选项卡组件,并通过JavaScript进行了功能封装,支持点击或鼠标悬停事件切换选项卡,以及自定义样式类名和切换结束后的回调函数。示例代码包括了选项卡的结构和样式,以及JavaScript插件的实现逻辑,旨在提高页面交互体验。
摘要由CSDN通过智能技术生成
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>珠峰培训</title>
    <link rel="stylesheet" href="css/reset.min.css">
    <style>
        /*==选项卡样式(用户决定)==*/
        .tabBox {
            margin: 20px auto;
            width: 360px;
        }

        .tabBox .option {
            height: 34px;
            border: 1px solid #DBDEE1;
            background: #EEE;
        }

        .tabBox .option li {
            float: left;
            padding: 0 12px;
            height: 34px;
            line-height: 34px;
        }

        .tabBox .option li a {
            display: block;
            color: #000;
            font-size: 14px;
        }

        .tabBox .option li.active {
            /*==插件中要求想让谁选中,给谁加ACTIVE样式类即可==*/
            border-top: 3px solid #FF8400;
            height: 33px;
            line-height: 33px;
            margin-top: -1px;
            background: #FFF;
        }

        .tabBox .con {
            display: none;
            padding-top: 10px;
            height: 220px;
            font-size: 14px;
            text-align: center;
        }

        .tabBox .con.active {
            display: block;
        }
    </style>
</head>
<body>
<!--
  1.外层容器包裹着整个选项卡
  2.页卡项都存储到样式类为OPTION的UL中,每一个页卡项都是一个LI(LI中放什么无所谓)
  3.每一个页卡对应的内容都在样式类为CON的DIV中(内容放什么无所谓)
  4.想让谁选中,就给谁加ACTIVE样式类(具体的选中样式用户自己规划,但是对于CON来说,开始是隐藏的,选中后是显示的)
-->
<div class="tabBox" id="tabBox">
    <ul class="option">
        <li class="active"><a href="#">图片</a></li>
        <li><a href="#">专栏</a></li>
        <li><a href="#">热点</a></li>
    </ul>
    <div class="con active">
        <!--每一个CON中存放什么用户自己确定-->
        1
    </div>
    <div class="con">2</div>
    <div class="con">3</div>
</div>

<div class="tabBox" id="tabBox2">
    <ul class="option">
        <li class="active"><a href="#">图片</a></li>
        <li><a href="#">专栏</a></li>
        <li><a href="#">热点</a></li>
        <li><a href="#">趣图</a></li>
    </ul>
    <div class="con active">
        <!--每一个CON中存放什么用户自己确定-->
        1
    </div>
    <div class="con">2</div>
    <div class="con">3</div>
    <div class="con">4</div>
</div id>

<script src="js/tabPlugin.js"></script>
<script>
    new TabPlugin(tabBox, {
        eventType: 'click'
    });

    new TabPlugin(tabBox2, {
        lastIndex: 2,
        changeEnd: function (curLi, curCon, index, lastIndex) {

        }
    });
</script>
</body>
</html>

 tab.js

// let tabBox = document.querySelector('#tabBox'),
//     option = tabBox.querySelector('.option'),
//     con = tabBox.querySelectorAll('.con');//=>querySelectorAll:是从当前上下文的所有后代元素中按照选择器规则进行筛选(而我们想要的是儿子中筛选)

//=>检测当前元素中是否包含某一个样式类名
let hasClass = (ele, str) => ele.className.trim().split(/ +/).indexOf(str) >= 0;

//=>给当前元素增加样式类名
let addClass = (ele, str) => {
    let isExit = hasClass(ele, str);
    if (isExit) return;//=>已经有这个样式类了,那么什么都没必要搞了
    ele.className += ` ${str}`;
};

//=>给当前元素移除样式类名
let removeClass = (ele, str) => {
    let isExit = hasClass(ele, str);
    if (!isExit) return;//=>不存在这个样式类,那还搞啥!
    let ary = ele.className.trim().split(/ +/);
    ary = ary.filter(item => item !== str);
    ele.className = ary.join(' ');
};

// let hasClass = function hasClass(ele, strClass) {
//     //1.首先获取ELE的现有的样式类名
//     let curClass = ele.className;
//     //2.把现有的样式类拆分成一个数组
//     let ary = curClass.trim().split(/ +/);
//     //3.验证数组中是否包含这一项即可
//     return ary.indexOf(strClass) >= 0;
// };

//=>获取需要的元素(精准获取)
let tabBox = document.querySelector('#tabBox'),
    childAry = [].slice.call(tabBox.children),
    option = null,
    optionList = null,
    conList = null;
/*option = childAry.filter((item, index) => {
    //=>我们不能直接用INDEX-OF检测当前元素中是否包含某一个样式类,因为INDEX-OF是只要包含这几个字符即可,我们需要是样式类名是完整的
    if (item.className.indexOf('option') >= 0) {
        return true;
    }
});*/
option = childAry.filter(item => hasClass(item, 'option'));
option = option.length > 0 ? option[0] : null;
optionList = [].filter.call(option.children, item => item.tagName === 'LI');
conList = childAry.filter(item => hasClass(item, 'con'));

//=>给获取的LI进行事件绑定
let lastIndex = 0;//=>上一个选择的索引
optionList.forEach((item, index) => {
    item.onmouseover = function anonymous() {
        if (lastIndex === index) return;
        //=>this:当前操作的LI
        //=>index:当前操作LI的索引
        addClass(this, 'active');
        removeClass(optionList[lastIndex], 'active');

        addClass(conList[index], 'active');
        removeClass(conList[lastIndex], 'active');

        lastIndex = index;
    };
});

tabPlugin.js

/*
 * 插件封装
 *   1.体现了封装的思想
 *   2.尽可能让用户操作简单,但是可以实现非常完善的效果(支持更多种业务可能)
 */
~function anonymous(window) {

    class TabPlugin {
        constructor(container, options = {}) {
            //=>第一个参数必传,而且传递的还需要是元素对象,如果匹配直接抛出异常信息,不让继续执行了(参数合法性验证)
            if (typeof container === 'undefined' || container.nodeType !== 1) {
                throw new SyntaxError('The first parameter is the item that must be passed, and it must be an element object type!');
            }

            //=>参数初始化(初始化配置项):把处理好的参数配置项尽可能的挂载到当前类的实例上,成为实例的私有属性,这样不仅在公共或者私有方法中直接可以获取使用,而且也保证每一个实例之间这些属性是不冲突的
            let _default = {
                lastIndex: 0,
                eventType: 'mouseover',
                customPageClass: 'option',
                customContentClass: 'con',
                changeEnd: null
            };
            for (let attr in options) {
                if (options.hasOwnProperty(attr)) {
                    _default[attr] = options[attr];//=>把OPTIONS传递进来的信息值覆盖_DEFAULT,此时_DEFAULT中存储的就是最新值
                }
            }
            for (let attr in _default) {
                if (_default.hasOwnProperty(attr)) {
                    this[attr] = _default[attr];
                }
            }

            //=>获取需要操作的元素,把获取的元素也挂载到实例上
            this.container = container;
            let childs = [...container.children],
                option = null;
            option = childs.find(item => this.hasClass(item, this.customPageClass));
            this.optionList = option ? [...option.children] : [];
            this.conList = childs.filter(item => this.hasClass(item, this.customContentClass));

            //=>让个LAST-INDEX对应项有选中样式,其余项没有选中样式
            this.optionList.forEach((item, index) => {
                if (index === this.lastIndex) {
                    this.addClass(this.optionList[index], 'active');
                    this.addClass(this.conList[index], 'active');
                    return;
                }
                this.removeClass(this.optionList[index], 'active');
                this.removeClass(this.conList[index], 'active');
            });

            //=>实现选项卡
            this.changeTab();
        }

        /*==把公共方法挂载到类的原型上==*/
        hasClass(ele, str) {
            return ele.className.trim().split(/ +/).indexOf(str) >= 0;
        }

        addClass(ele, str) {
            //=>hasClass()不能直接调取,需要基于实例调取使用(或者直接基于类来调取使用也可以 TabPlugin.prototype.hasClass())
            if (this.hasClass(ele, str)) return;
            ele.className += ` ${str}`;
        }

        removeClass(ele, str) {
            if (!this.hasClass(ele, str)) return;
            ele.className = ele.className.trim().split(/ +/).filter(item => item !== str).join(' ');
        }

        changeTab() {
            this.optionList.forEach((item, index) => {
                //=>THIS:实例
                let _this = this;
                item[`on${this.eventType}`] = function anonymous() {
                    //=>THIS:当前操作的LI
                    if (_this.lastIndex === index) return;
                    _this.addClass(this, 'active');
                    _this.removeClass(_this.optionList[_this.lastIndex], 'active');

                    _this.addClass(_this.conList[index], 'active');
                    _this.removeClass(_this.conList[_this.lastIndex], 'active');

                    _this.lastIndex = index;

                    //=>切换完成后执行传递进来的回调函数(回调函数中的THIS是当前类的实例,把当前切换这一项索引和上一项的索引传递给回调函数,还把当前操作的LI以及操作的CON也都传给回调函数了)
                    _this.changeEnd && _this.changeEnd(this, _this.conList[index], index, _this.lastIndex);
                };
            });
        }
    }

    window.TabPlugin = TabPlugin;
}(window);
// new TabPlugin([container], [options配置项对象]);

/*
 * 不确定项
 *   1.哪个容器实现选项卡
 *   2.默认选中项(参考值:0 第一个选中)
 *   3.切换的事件类型(参考值:mouseover 鼠标滑过切换)
 *   4.可以自定义页卡区域的样式类和内容区域的样式类(参考值:option/con)
 *   5.支持钩子函数(生命周期函数),例如:我们可以支持切换完成后做什么事,你只需要传递给我一个回调函数,在内部插件每一次切换完成后,我把传递的回调函数执行
 *   ...
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值