<!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.支持钩子函数(生命周期函数),例如:我们可以支持切换完成后做什么事,你只需要传递给我一个回调函数,在内部插件每一次切换完成后,我把传递的回调函数执行
* ...
*/