Case style:
Functional requirements:
- Click the tab bar to switch the effect
- Click the + sign to add tab items and content items
- click × Number, you can delete the current tab item and content item
- Double click the text of tab item or content item to modify the text content
Extracting objects: Tab objects
- The object has the function of switching
- This object has the function of adding
- The object has deletion function
- This object has modification function
Get all elements:
var that; class Tab { constructor(id){ //Get element that = this; this.main = document.querySelector(id); this.add = this.main.querySelector('.tabadd'); //Parent element of li this.ul = this.main.querySelector('.firstnav ul:first-child'); //Parent element of section this.fsection = this.main.querySelector('.tabscon'); this.init();//init is called through the instance object } init (){ this.updateNode(); // init initializes the operation and lets the related elements bind events this.add.onclick = this.addTab; for (var i = 0; i < this.lis.length; i++){ this.lis[i].index = i; this.lis[i].onclick = this.toggleTab; } } //Get all small li and section s updateNode(){ this.lis = this.main.querySelectorAll('li'); this.sections = this.main.querySelectorAll('section'); }
Switching function:
Click different li to switch the tabs
//1. Switching function toggleTab(){ // console.log(this.index); that.clearClass(); this.className = 'liactive'; that.sections[this.index].className = 'conactive'; } clearClass (){ for (var i = 0; i <this.lis.length; i++){ this.lis[i].className = ''; this.sections[i].className = ''; } }
Add features:
- Click + to add new tabs and contents
- Step 1: create a new tab li and a new content section
- Step 2: append the two created elements to the corresponding parent element
- Previous practice: dynamically create the element createElemnet, but there are many contents in the element. innerHTML assignment is required and appended to the parent element in appendChild
- Now advanced approach: using insertAdjacentHTML(), you can directly add string format elements to the parent element
- appendChild does not support appending sub elements of strings. insertAdjanceHTML supports appending elements of strings
//2. Add functions addTab(){ that.clearClass();//Clear all li and section classes //(1) Create li element and section element var random = Math.random(); var li = ' <li class="liactive"><span>new tab </span><span class="iconfont icon-guanbi"></span></li>'; var section = ' <section class="conactive">test'+ random +'</section>'; //(2) Append these two elements to the corresponding parent element that.ul.insertAdjacentHTML('beforeend',li); that.fsection.insertAdjacentHTML('beforeend',section); that.init();//Solve the problem that the added element has no click event }
Delete function
Click × You can delete the current li tab and the current section
× There is no index number, but its father li has an index number. This index number is exactly what we want
So the core idea is: click x to delete the li and section corresponding to the index number
//3. Delete function removeTab(e){ e.stopPropagation();//Prevent bubbling and trigger switching click events of li var index = this.parentNode.index; console.log(index); //Delete the corresponding li and section remove methods according to the index number to directly delete the specified element that.lis[index].remove(); that.sections[index].remove(); that.init(); //When we delete a li that is not in the selected state, the li in the original selected state remains unchanged if(document.querySelector('.liactive')) return; //When we delete the li in the selected state, we make the previous li in the selected state index--; //Manually call the click event without mouse triggering that.lis[index] && that.lis[index].click(); }
Edit operation
- Double click the text in the tab li or section to realize the modification function
- The double click event is: ondbllick
- If you double-click the text, the text will be selected by default. At this time, you need to double-click to prohibit the selection of text
- window.getSelection?window.getSelection().removeAllRanges():document.selection.empty();
- Core idea: when you double-click the text, a text box is generated inside. When you lose focus or press enter, and then give the value entered in the text box to the original element.
//4. Modify function editTab(){ var str = this.innerHTML; //Double click to suppress the selected text window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); this.innerHTML = '<input type="text" />'; var input = this.children[0]; input.value = str; input.select();//Make the text in input in the selected state input.onblur = function(){ this.parentNode.innerHTML = this.value; //Press enter to return the value in the text box to span input.onkeyup = function(e){ if (e.keyCode === 13){ //Manually calling the event that the form loses focus does not require the mouse to leave the operation this.blur(); } } } }
Full code:
HTML part
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="../css/tab.css"> </head> <body> <main> <h4>JS Object oriented dynamic adding tabs</h4> <div class="tabsbox" id="tab"> <!-- tab label --> <nav class="firstnav"> <ul> <li class="liactive"><span>Test 1</span><span class="icon-cross"></span></span></li> <li><span>Test 2</span><span class="icon-cross"></span></span></li> <li><span>Test 3</span><span class="icon-cross"></span></span></li> </ul> <div class="tabadd"> <span>+</span> </div> </nav> <!-- tab content --> <div class="tabscon"> <section class="conactive">Test 1</section> <section>Test 2</section> <section>Test 3</section> </div> </div> </main> <script src="../js/tab.js"></script> </body> </html>
CSS part
* { margin: 0; padding: 0; } @font-face { font-family: 'icomoon'; src: url('fonts/icomoon.eot?87vq05'); src: url('fonts/icomoon.eot?87vq05#iefix') format('embedded-opentype'), url('fonts/icomoon.ttf?87vq05') format('truetype'), url('fonts/icomoon.woff?87vq05') format('woff'), url('fonts/icomoon.svg?87vq05#icomoon') format('svg'); font-weight: normal; font-style: normal; font-display: block; } ul li { list-style: none; } main { width: 960px; height: 500px; border-radius: 10px; margin: 50px auto; } main h4 { 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 { font-family: 'icomoon'; 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: 2px solid #fff; z-index: 9; } nav ul li:hover { background-color: #ccc; } main ul li .icon-cross { font-family: 'icomoon'; } #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; }
Note: the font Icon needs to be introduced into the small cross part, and attention should be paid to reference and change when using it.
JavaScript part
var that; class Tab { constructor(id){ //Get element that = this; this.main = document.querySelector(id); this.add = this.main.querySelector('.tabadd'); //Parent element of li this.ul = this.main.querySelector('.firstnav ul:first-child'); //Parent element of section this.fsection = this.main.querySelector('.tabscon'); this.init();//init is called through the instance object } init (){ this.updateNode(); // init initializes the operation and lets the related elements bind events this.add.onclick = this.addTab; for (var i = 0; i < this.lis.length; i++){ this.lis[i].index = i; this.lis[i].onclick = this.toggleTab; this.remove[i].onclick = this.removeTab; this.spans[i].ondblclick = this.editTab; this.sections[i].ondblclick = this.editTab; } } //Because we add elements dynamically, we need to retrieve the corresponding elements updateNode(){ this.lis = this.main.querySelectorAll('li'); this.sections = this.main.querySelectorAll('section'); this.remove = this.main.querySelectorAll('.icon-cross'); this.spans = this.main.querySelectorAll('.firstnav li span:first-child'); } //1. Switching function toggleTab(){ // console.log(this.index); that.clearClass(); this.className = 'liactive'; that.sections[this.index].className = 'conactive'; } clearClass (){ for (var i = 0; i <this.lis.length; i++){ this.lis[i].className = ''; this.sections[i].className = ''; } } //2. Add functions addTab(){ that.clearClass();//Clear all li and section classes //(1) Create li element and section element var random = Math.random(); var li = ' <li class="liactive"><span>New options</span><span class="icon-cross"></span></span></li>'; var section = ' <section class="conactive">test'+ random +'</section>'; //(2) Append these two elements to the corresponding parent element that.ul.insertAdjacentHTML('beforeend',li); that.fsection.insertAdjacentHTML('beforeend',section); that.init();//Solve the problem that the added element has no click event } //3. Delete function removeTab(e){ e.stopPropagation();//Prevent bubbling and trigger switching click events of li var index = this.parentNode.index; console.log(index); //Delete the corresponding li and section remove methods according to the index number to directly delete the specified element that.lis[index].remove(); that.sections[index].remove(); that.init(); //When we delete a li that is not in the selected state, the li in the original selected state remains unchanged if(document.querySelector('.liactive')) return; //When we delete the selected li, the previous li will be selected index--; //Manually call the click event without mouse triggering that.lis[index] && that.lis[index].click(); } //4. Modify function editTab(){ var str = this.innerHTML; //Double click to suppress the selected text window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); this.innerHTML = '<input type="text" />'; var input = this.children[0]; input.value = str; input.select();//Make the text in input in the selected state input.onblur = function(){ this.parentNode.innerHTML = this.value; //Press enter to return the value in the text box to span input.onkeyup = function(e){ if (e.keyCode === 13){ //Manually calling the event that the form loses focus does not require the mouse to leave the operation this.blur(); } } } } } new Tab('#tab');