使用class类打包tab栏切换
一、搭建html结构
<main>
<h4 style="user-select: none;">JS 面向对象,动态添加标签页</h4>
<div class="tabsbox" id="tab">
<!-- tab标签 -->
<nav class="firstnav">
<!-- tab栏标题 -->
<ul>
<li class="liactive"><span>测试1</span><span class="close">×</span></li>
<li><span>测试2</span><span class="close">×</span></li>
<li><span>测试3</span><span class="close">×</span></li>
</ul>
<!-- 添加按钮 -->
<div class="tabadd">
<span>+</span>
</div>
</nav>
<!-- tab 内容 -->
<div class="tabscon">
<section class="conactive">内容1</section>
<section>内容2</section>
<section>内容3</section>
</div>
</div>
</main>
效果图
二、css 样式
* {
margin: 0;
padding: 0;
}
main {
width: 900px;
margin: 0px auto;
}
h4 {
text-align: center;
line-height: 50px;
}
.tabsbox {
width: 800px;
margin: 0 auto;
border: 1px solid orange;
}
.firstnav {
position: relative;
line-height: 40px;
height: 40px;
text-align: center;
border-bottom: 1px solid #999;
}
.firstnav li {
list-style: none;
width: 100px;
float: left;
border-right: 1px solid #999;
position: relative;
cursor: pointer;
}
.firstnav li span {
/* 阻止用户选中文字 */
user-select: none;
}
.firstnav li.liactive::after {
content: "";
width: 100%;
height: 2px;
position: absolute;
z-index: 11;
bottom: -2px;
left: 0;
background: #fff;
}
.firstnav .close {
cursor: pointer;
position: absolute;
right: 2px;
top: 2px;
font-size: 14px;
color: #666;
border: 1px solid #ccc;
width: 14px;
height: 14px;
line-height: 12px;
text-align: center;
border-radius: 100%;
}
.tabscon {
height: 300px;
white-space: normal;
}
.tabscon input {
width: 100%;
height: 80px;
}
.tabscon section {
padding: 30px;
display: none;
}
.tabscon section.conactive {
display: block;
}
.tabadd {
position: absolute;
padding: 0 10px;
right: 10px;
top: 0px;
font-size: 20px;
cursor: pointer;
user-select: none;
}
input {
width: 50px;
line-height: 20px;
}
三、js
// 用来保存类中的 this
let that
// 1. 定义 Tab 栏类
class Tab {
constructor(selector) { // 这里的 selector = #tab
// 2. 定义公共的属性
this.main = document.querySelector(selector) //tab 栏主容器
this.ul = this.main.querySelector('ul') //标题栏
this.add = this.main.querySelector('.tabadd') //添加按钮
this.content = this.main.querySelector('.tabscon') //内容区域
this.lis = this.ul.children //所有的标题
this.secs = this.content.children //所有的内容
// 4. 在页面加载完的时候就调用初始化 init 方法(因为类似于点击切换这种操作要在一开始就调用)
this.init()
// 将类属性定义中的 this 保存到 that 变量中去
that = this // that 表示利用 Tab 栏类创建的对象
}
// 4. 初始化
init() {
// 遍历所有的 li ,注册事件
for (let i = 0; i < this.lis.length; i++) {
// 循环给 li 注册点击事件
this.lis[i].addEventListener('click', this.toggleTab)
// 5.1 同时将 li 的索引号保存到当前的元素对象中去,作为一个属性存在
this.lis[i].index = i
// 给删除按钮注册点击事件
this.lis[i].children[1].addEventListener('click', this.deleteTab)
// 给标题的 span 标签注册双击事件
this.lis[i].children[0].addEventListener('dblclick', this.editorTab)
this.content.children[i].addEventListener('dblclick', this.editorTab)
}
// 给 新增按钮 注册点击事件
this.add.addEventListener('click', this.addTab)
}
// 3.公共的方法:增加,删除,切换,修改
// 切换
toggleTab() {
// 因为这里的 this 已经指向了当前选中的 li ,所以使用 全局变量 that 作为媒介
// 遍历所有的 li
for (let i = 0; i < that.lis.length; i++) {
// 排他思想
// 删除所有 li 的类名
that.lis[i].classList.remove('liactive')
// 删除所有 content 的类名
that.secs[i].classList.remove('conactive')
}
this.classList.add('liactive')
// 5.2 选中当前应该显示的 content ,通过类名让当前 content 显示
that.secs[this.index].classList.add('conactive')
}
// 增加
addTab() {
// 点击增加一个子元素
that.ul.innerHTML += '<li><span>new tab</span><span class="close">×</span></li>'
that.content.innerHTML += '<section>新的内容页</section>'
// 因为在增加子元素的时候,页面重绘了,所以重启初始化
that.init()
// 默认显示新增的内容:模拟点击一次最后的 tab标题
that.ul.lastElementChild.click()
// that.lis[that.lis.length - 1].click()
}
// 删除
deleteTab(e) {
e.stopPropagation()
let index = this.parentNode.index
if(this.parentNode.className == 'liactive'){
if(index != 0){
that.lis[index - 1].click()
}else{
that.lis[index + 1].click()
}
}
that.ul.removeChild(this.parentNode)
that.content.removeChild(that.secs[index])
that.init()
}
//编辑
editorTab() {
this.innerHTML = '<input type="text" value="'+this.innerText+'">'
this.children[0].select()
this.children[0].addEventListener('blur',function() {
this.parentNode.innerText = this.value
})
this.children[0].addEventListener('keyup',function(e) {
if (e.keyCode === 13){
this.blur()
}
})
}
}
new Tab('#tab')
四、注意点
- 获取元素其实就是在构造函数里面添加属性
- 实现动态增删改查就是类方法
- init 初始化可以在页面已加载完成就绑定完成事件,不需要手动调用
- 注意类中的调用自身属性或方法前面一定要加 this
- 注意 this 的指向问题,函数看调用,事件看绑定者
- 注意冒泡事件对代码执行的影响