前言
上篇说到:如何实现选项卡。那么实现选项卡的目的是为了在展示更多内容·的同时,减少翻页成本。大部分文档都是以选项卡形式呈现,它就像一本书我们可以看到它的目录。常见的二级列表三级列表是它复杂的延伸。如何将其封装成一个功能,通过数据渲染就可以实现昵,那么今天就来实现。
一、思路分析
1,我需要的是一个类似字典结构的文档展示页面
2,它不需要数据回流只做展示
3,我只需要将数据模型放进去就可以自动生成页面
4,我需要实现一个两级的选项页,顶部是专栏,左侧是细分,中间是内容
二,实现过程
样式及结构
.app{
position: relative;
/* width: 100vw; */
/* background-color: burlywood; */
}
.top{
position: absolute;
width: 100vw;
height: 100px;
/* background-color: #4169E1; */
/* overflow-x: scroll; */
/* overflow-y: hidden; */
}
.left{
position: absolute;
width: 100px;
left: 0;
/* background-color: #4169E1; */
}
.left-tap{
width: 100px;
height: 50px;
text-align: center;
line-height: 50px;
color: white;
margin-top: 2px;
background-color: pink;
}
.left-tap:hover{
background-color: #008000;
}
.right{
position: absolute;
right: 0;
width: 80vw;
height: 100px;
box-sizing: border-box;
padding: 20px;
}
.right-content{
height: 50px;
width: 100%;
font-size: 30px;
color: orchid;
}
.bottom{
position: absolute;
left: 50%;
width: 90vw;
transform: translate(-50%);
top:120px;
height: 100px;
/* display: flex;
justify-content: space-between; */
}
.top-tab{
float: left;
width: 150px;
text-align: center;
line-height: 100px;
height: 100px;
margin-right: 5px;
border-top-right-radius: 20px;
background-color: green;
font-size: 20px;
color: white;
}
<div class="app" id="app">
</div>
结构就只有一个根结点,通过createElement()创建
*调用代码
window.onload = function(){
new Tab("app").init(list,2).click1().click2();
}
数据模型
const list = [
{
"HTML":[
{
"div":[
"div是一个盒模型,是构成网页内容的基本盒子",
"是块元素,独占一行"
]
},
{
"p":[
"p标签是行内块元素,默认情况下独占一行",
"p标签不可嵌套p标签"
]
},
{
"input":[
"input内容输入标签,可设置多种type",
"功能强大"
]
}
],
"CSS":[
{
"简介":[
"css层叠样式表是书写网页样式的重要部分",
"有多种选择器"
]
},
{
"选择器":[
"类选择器",
"标签选择器",
"id选择器"
]
},
{
"属性":[
"color设置文本内容颜色",
"height元素高度",
"transition过渡效果"
]
}
],
"JAVASCRIPt":[
{
"环境":[
"js目前仍是网页展示的主流脚本",
"node平台和ES6的普及大大提高了js的功能性",
"它能做的事情很多包括智能设备"
]
},
{
"变量":[
"var,let,const变量的三种声明方式",
"变量的本质是作用域上的键",
"其作用是将相同的数据引用到不同的地方,如果引用数据最好使用变量接收"
]
},
{
"数据类型":[
"基本数据类型",
"引用数据类型"
]
}
],
}
]
封装
function Tab(id){
this.app = document.getElementById(id);
this.topDiv = document.createElement("div");
this.bottomDiv = document.createElement("div");
this.leftDiv = document.createElement("div");
this.rightDiv = document.createElement("div");
this.app.appendChild(this.topDiv);
this.app.appendChild(this.bottomDiv);
this.bottomDiv.appendChild(this.leftDiv);
this.bottomDiv.appendChild(this.rightDiv);
}
Tab.prototype.init = function(list=[],index=0,place=0){
if(this.leftDiv.className) return;//避免重复渲染
this.topDiv.classList.add('top');
this.bottomDiv.classList.add('bottom');
this.leftDiv.classList.add('left');
this.rightDiv.classList.add('right');
for(var i = 0;i<list.length;i++){
//取得一级结构主键生成专栏
let oneTap = Object.keys(list[i]);
let leftCont = [];
for(var k = 0;k<oneTap.length;k++){
let elementTop = document.createElement('duv');
elementTop.innerHTML = oneTap[k];
elementTop.classList.add('top-tab');
if(k === index){
elementTop.style.backgroundColor = "yellow";
}
this.topDiv.appendChild(elementTop);
}
//遍历每个主键的内容,生成侧边栏
for(let key in list[i]){
// console.log(key);
for(var j = 0;j < list[i][key].length; j++){
let leftTap = document.createElement("div");
leftTap.classList.add('left-tap');
leftTap.innerText = Object.keys(list[i][key][j]);
leftTap.keys = Object.keys(list[i][key][j])[0];
if(oneTap[index] === key){
leftTap.style.display = 'block';
leftCont.push(Object.keys(list[i][key][j])[0]);
if(j === place){
leftTap.style.backgroundColor = 'yellow';
}
}else{
leftTap.style.display = 'none';
}
this.leftDiv.appendChild(leftTap);
//遍历生成内容
for(var p = 0;p < list[i][key][j][Object.keys(list[i][key][j])].length;p++){
let contentDiv = document.createElement("div");
contentDiv.classList.add('right-content');
contentDiv.innerText = list[i][key][j][Object.keys(list[i][key][j])][p];
contentDiv.cont = list[i][key][j][Object.keys(list[i][key][j])][p];
contentDiv.level = Object.keys(list[i][key][j])[0];
if(oneTap[index] === key&&Object.keys(list[i][key][j])[0] === leftCont[0]){
if(j === place){
contentDiv.style.display = 'block';
}
}else{
contentDiv.style.display = 'none';
}
this.rightDiv.appendChild(contentDiv);
}
}
}
}
return this;
}
Tab.prototype.click1 = function(){
let oneTap = Object.keys(list[0]);
let topTap = this.topDiv.children;
let leftTap = this.leftDiv.children;
let contentDiv = this.rightDiv.children;
for(let i = 0;i < topTap.length;i++){
topTap[i].key = i;
topTap[i].onclick = function(){
let key = this.key;
let keyArr = [];
let optTapArr = [];
let contArr = [];
//点击态
for(var k = 0 ;k < topTap.length;k++){
if(key === k){
topTap[k].style.backgroundColor = "yellow";
}else{
topTap[k].style.backgroundColor = "green";
}
}
//数据 对应的键
for(var k = 0;k < list[0][oneTap[key]].length;k++){
keyArr.push(Object.keys(list[0][oneTap[key]][k])[0])
}
//筛选应该显示的键
for(var k = 0 ;k < leftTap.length;k++){
for(var j = 0 ;j < keyArr.length;j++){
if(keyArr[j] === leftTap[k].keys){
optTapArr.push(leftTap[k]);
}else{
leftTap[k].style.display = 'none';
leftTap[k].style.backgroundColor = 'pink';
}
}
}
for(var k = 0;k < optTapArr.length;k++){
optTapArr[k].style.display = "block";
}
//默认选择第一个
optTapArr[0].style.backgroundColor = 'yellow';
//对应的内容也要展示第一个的
for(var k = 0;k < contentDiv.length;k++){
if(optTapArr[0].keys === contentDiv[k].level){
contentDiv[k].style.display = "block";
}else{
contentDiv[k].style.display = "none";
}
}
}
}
return this;
}
Tab.prototype.click2 = function(){
let leftTap = this.leftDiv.children;
let contentDiv = this.rightDiv.children;
for(let i = 0;i < leftTap.length; i++){
leftTap[i].key = i;
leftTap[i].onclick = function(){
let keys = this.keys;
let key = this.key;
for(var k = 0;k < leftTap.length; k++){
leftTap[k].style.backgroundColor = 'pink';
}
this.style.backgroundColor = "yellow";
for(var k = 0;k < contentDiv.length; k++){
if(keys === contentDiv[k].level){
contentDiv[k].style.display = "block";
}else{
contentDiv[k].style.display = "none";
}
}
}
}
return this;
}
效果
总结
整个实现过程较为粗糙,细节需要打磨,在生成dom结构的时候,我就应该生成和数据文档一样的dom结构。这样可以提高效率和便于后续操作。数据模型写的有些问题,我应该直接使用对象为最外层。在这个过程中我一点虚拟dom的思路了。还可以做一个标识,当切回来的时候保持原有状态。后续会写。