本文用JavaScript面向对象思想写tab栏
本文旨在对JavaScript面向对象加深印象,练习下思路
本文核心知识点:
- JavaScript面向对象的思想
- 面向对象中this指向问题
1. 功能需求
- 点击 tab栏,可以切换效果.
- 点击 + 号, 可以添加 tab 项和内容项.
- 点击 x 号, 可以删除当前的tab项和内容项.
- 双击tab项文字或者内容项文字可以修改里面的文字内容
- 细节处做优化(避免tab填满溢出,删完报错等)
2.效果预览
3.demo准备
- 获取到标题元素
- 获取到内容元素
- 获取到删除的小按钮 x号
- 新建js文件,定义类,添加需要的属性方法(切换,删除,增加,修改)
- 时刻注意this的指向问题
4.功能完善
4.1. 切换
-
为获取到的标题绑定点击事件,展示对应的内容区域,存储对应的索引
-
使用排他,实现只有一个元素的显示
4.2.添加
-
为添加按钮+ 绑定点击事件
-
实现标题与内容的添加,做好排他处理
4.3.删除
-
为元素的删除按钮x绑定点击事件
-
获取到点击的删除按钮的所在的父元素的所有,删除对应的标题与内容
4.4.编辑
-
为元素(标题与内容)绑定双击事件
-
在双击事件处理文本选中状态,修改内部DOM节点,实现新旧value值的传递
4.5.处理细节
- 对于代码进行适当优化
- 细小问题及时发现并处理
完整js代码(注释超清晰)
window.addEventListener('load', function() {
// 定义变量改变this指向
var that;
// 创建类(tab)
class Tab {
constructor(id) {
that = this;
// 获取元素
this.main = document.querySelector(id);
this.addBtn = this.main.querySelector('.tabadd');
this.ul = this.main.querySelector('.fisrstnav ul');
this.tabscon = this.main.querySelector('.tabscon');
this.init();
}
// 初始化操作,每次刷新重新渲染
init() {
// 加载动态生成的元素
this.updataNode();
// 点击添加
this.addBtn.onclick = this.add;
// 给每个li绑定点击事件
for (var i = 0; i < this.lis.length; i++) {
// 为每个li绑定索引,后续操作便捷
this.lis[i].index = i;
this.lis[i].onclick = this.toggleTab;
this.removeBtns[i].onclick = this.remove;
this.spans[i].ondblclick = this.edit;
this.sections[i].ondblclick = this.edit;
}
}
// 用于重新加载那些动态生成的元素
updataNode() {
this.lis = this.main.querySelectorAll('li');
this.sections = this.main.querySelectorAll('section');
this.removeBtns = this.main.querySelectorAll('.iconfont');
this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
}
// 清除之前的类(样式)
clearClass() {
for (var i = 0; i < this.lis.length; i++) {
this.lis[i].className = '';
this.sections[i].className = '';
}
}
// tab栏切换
toggleTab() {
// 先删除所有样式
that.clearClass();
// 当前点击添加样式
this.className = 'liactive';
that.sections[this.index].className = 'conactive';
}
// 添加功能
add() {
var ulWidth = this.parentNode.offsetWidth;
// 清除所有样式,留自己
that.clearClass();
// 生成随机数,分配给内容
var random = Math.random();
// 创建新选项卡及内容并追加
var li = '<li class="liactive"><span>newTab</span><span class="iconfont icon-guanbi"></span></li>';
var section = '<section class="conactive">测试' + random + '</section>';
// 判断大盒子宽度,限制增加个数
// 每个li的宽度(兼容性高的写法)
// console.log(this.parentNode.children[0].children[0].offsetWidth);
var liWidth = this.previousElementSibling.children[0].offsetWidth;
if (liWidth * that.lis.length < ulWidth - liWidth) {
that.ul.insertAdjacentHTML('beforeend', li);
that.tabscon.insertAdjacentHTML('beforeend', section);
} else {
alert('选项卡已达上限')
}
// 重新初始化,加载动态生成的元素
that.init();
}
// 删除功能
remove(e) {
// 阻止冒泡,防止点击影响li的样式
e.stopPropagation();
// 点击删除图标拿到当前li及模块的索引
var index = this.parentNode.index;
// 根据索引删除对应的li及模块
that.lis[index].remove();
// that.sections[index].remove();(复习removeChild方法)
that.tabscon.removeChild(that.sections[index]);
// 删除后让此li前面的li添加选中样式(手动触发浅表的li点击事件)
// 如果我们删除了非选定状态的li,之前选定状态不变
if (document.querySelector('.liactive')) return;
// 否则index进行自减
index--;
// 防止index < 0(做判断)
that.lis[index] && that.lis[index].click();
// 删除后重新渲染元素,保持元素动态变换
that.init();
}
// 修改功能
edit() {
// 双击禁止选定文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
// 获取当前span内的内容
var str = this.innerHTML;
// 改变点击的span的内部元素并传入value
this.innerHTML = '<input type="text" value=' + str + '>';
// 获得这个input
var input = this.children[0];
// 让input内容被选中
input.select();
// 当失去焦点,内容修改成功,内容给span
input.onblur = function() {
// 判断输入内容是否为空
// if (this.value.trim().length == 0) {
// alert('tab不能为空')
// } else {
// this.parentNode.innerHTML = this.value;
// }
this.value.trim().length == 0 ? alert('tab不能为空') : this.parentNode.innerHTML = this.value;
};
// 如果敲击回车,也可修改内容
input.onkeyup = function(e) {
if (e.keyCode === 13) {
// 手动触发失去焦点事件
this.blur()
}
}
}
}
// 实例化对象
new Tab("#tab")
})
最后:html及css部分在下惭愧,拿不出手,相信大家写的会比我好,奥利给。