我们的浏览器顶部就是类似于一个tab栏的结构
这里我们可以直接将这个模块剖析成四个功能部分
标题栏增添,删除,切换和修改内容
标题栏和下面的内容栏是同步的,一个动态变化的过程
当我们点击切换的时候里面的内容也会切换,这是我们还可以修改里面的内容
基于ES6的一些语法,主要是运用类的知识可以写一个这样的简单小案例
首先是网页布局问题:
简单的HTML代码,一个标题标签,下面是一个大的div包裹着上下两个部分,标题栏和内容栏,代码如下:
<main>
<h1>414寝室</h1>
<div class="tabsbox" id="tab">
<!--tab标签-->
<nav class="firstnav">
<ul>
<li class="liactive"><span>测试一</span><span class="iconfont"></span></li>
<li><span>测试二</span><span class="iconfont"></span></li>
<li><span>测试三</span><span class="iconfont"></span></li>
</ul>
<div class="tabadd">
<span>+</span>
</div>
</nav>
<!--tab内容-->
<div class="tabscon">
<section class="conactive">测试一</section>
<section>测试二</section>
<section>测试三</section>
</div>
</div>
</main>
接下来就是css样式的问题,也没什么难度,就是要注意一个点,在上下两个部分的交汇处边框衔接不流畅,我们需要的是选中的标签栏应该和下面的内容栏成联通状态,如果设置上面边框为白色或者下面边框为白色是不能将另外一个颜色覆盖的,所以这个时候我们就需要设置background-color为白色,这样才能掩盖下面边框的颜色。
这是完整的css代码(懒得创建外部文件于是写在了网页代码里)
<style>
* {
margin: 0;
padding: 0;
}
ul li {
list-style: none;
}
main {
width: 960px;
height: 500px;
border-radius: 10px;
margin: 50px auto;
}
main h1 {
height: 100px;
line-height: 100px;
text-align: center;
}
.tabsbox {
width: 900px;
margin: 0 auto;
height: 400px;
border: 1px solid lightsalmon;
position: relative;
}
nav ul {
overflow: hidden;
}
nav ul li {
float: left;
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
border-right: 1px solid #ccc;
position: relative;
}
nav ul li.liactive {
border-bottom: 1px solid #fff;
z-index: 10;
background-color: white;
}
#tab input {
width: 80%;
height: 60%;
}
nav ul li span:last-child {
position: absolute;
user-select: none;
font-size: 12px;
top: -18px;
right: 0;
display: inline-block;
height: 20px;
}
.tabadd {
position: absolute;
/* width: 100px; */
top: 0;
right: 0;
}
.tabadd span {
display: block;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
border: 1px solid #ccc;
float: right;
margin: 10px;
user-select: none;
}
.tabscon {
width: 100%;
height: 300px;
position: absolute;
padding: 30px;
top: 50px;
left: 0px;
box-sizing: border-box;
border-top: 1px solid #ccc;
}
.tabscon section,
.tabscon section.conactive {
display: none;
width: 100%;
height: 100%;
}
.tabscon section.conactive {
display: block;
}
</style>
刚刚说的要注意的点就是上面nav ul li.liactive的样式。
接下来就是最重要的js代码了:
//功能需求
//1.点击tab栏,可以切换效果
//2.点击+号,可以添加tab项和内容项
//3.点击*号,可以删除当前tab项和内容项,核心思路是x号父元素索引对应的li和section的删除
//4.双击tab项文字或内容相文字可以修改里面的文字内容
//抽象对象:tab对象
//该对象具有切换,增删改功能
var that
class Tab{
constructor(id){
//获取元素
that=this
this.main=document.querySelector(id)
this.lis=this.main.querySelectorAll('li')//注意加this,获取大盒子里面的小盒子
this.sections=this.main.querySelectorAll('section')
this.add=this.main.querySelector('.tabadd')
//获取删除按钮
this.remove=this.main.querySelectorAll('.iconfont')
//li的父元素
this.ul=this.main.querySelector('.firstnav ul:first-child')
//section的父元素
this.tabscon=this.main.querySelector('.tabscon')
//span变文本框按钮
this.spans=this.main.querySelectorAll('.firstnav li span:first-child')
this.init() //初始化放在这里,外部new的时候自动调用
}
//初始化操作让相关元素绑定事件
init(){
this.updateNode() //每次初始化都用一次
this.add.οnclick=this.addTab
for(let i=0;i<this.lis.length;i++){
this.lis[i].index=i
this.lis[i].οnclick=this.toggleTab
//绑定删除操作
this.remove[i].οnclick=this.removeTab
//修改文本框的操作
this.spans[i].οndblclick=this.editTab
this.sections[i].οndblclick=this.editTab
}
}
//清楚所有li和section的类
clearClass(){
for(let i =0;i<this.lis.length;i++){
this.lis[i].className=''
this.sections[i].className=''
}
}
//每次点击添加或者删除时,重新获取所有li和section的函数,不然就会有bug
updateNode(){
this.lis=this.main.querySelectorAll('li')//注意加this,获取大盒子里面的小盒子
this.sections=this.main.querySelectorAll('section')
this.remove=this.main.querySelectorAll('.iconfont')
this.spans=this.main.querySelectorAll('.firstnav li span:first-child')
}
//1.切换功能
toggleTab(){
that.clearClass()
this.className='liactive'
that.sections[this.index].className='conactive' //利用全局变量that保存constructor里面的this
}
//2.添加功能
addTab(){ ///新方法,比createElement,append child的方法简单
//每次调用都清除一下
that.clearClass()
//1.创建li元素和section元素
let li='<li class="liactive"><span>新标签栏</span><span class="iconfont"></span></li>'
let section='<section class="conactive">新内容栏</section>'
//2.把这两个元素追加到对应的父元素里面
that.ul.insertAdjacentHTML('beforeend',li) //参数是插入位置和内容
that.tabscon.insertAdjacentHTML('beforeend',section)
that.init() //添加完后就执行一次重新获取标签
}
//3.删除功能
removeTab(event){
event.stopPropagation() //阻止冒泡
//获取父级元素索引来删除标题栏
let index=this.parentNode.index
that.lis[index].remove()
that.sections[index].remove()
that.init()
//如果删除的不是选中的元素,则选中的元素不变
if(document.querySelector('.liactive')) return
//删除一个元素时。让前一个元素处于选中状态,用click,自动执行一次点击
index--
if(index==-1)
index++
that.lis[index] && that.lis[index].click()
that.sections[index] && that.sections[index].click()//如果为真,才执行后面的语句
}
//4.修改,编辑功能
//核心思路:双击文字在里面生成文本框,当失去焦点或按下回车时将最新的文本框内容赋值给原先的元素
editTab(){
let str = this.innerHTML
//双击禁止选定文字,不用记
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
//双击生成文本框
this.innerHTML='<input type="text">'
let input = this.children[0]
input.value=str
input.select() //这个方法让文本框的文字处于选定状态
//离开文本框就把文本框的值赋值给span
input.οnblur=function(){ //这个时失去焦点的事件(文本框)
this.parentNode.innerHTML=this.value
}
//写一个按下回车键也可以复制的代码
input.οnkeyup=function(event){
if(event.keyCode===13)
this.blur()
}
}
}
new Tab('#tab')//把整个大的div作为参数对象传进去
注释都写的超级清清楚楚呢,卡哇伊~~~~~~
这里就有一个想要吐槽的点了,就是js语法中居然不能将自增自减符当作数组的索引来用,真是太恶心心了(叠词恶心心啦)*********
可以把代码复制过去验证一下哦!
---------------------------------启发来自pink老师