1.实现的页面效果

javascript 增加标签 js动态添加标签_javascript

2.JS相关代码实现

(1)实现功能要求

  1. 增删查改功能
  2. 双击改tab栏文字内容
  3. 用JS面向对象的思路

(2)实现步骤

1. 搭建最大的那个框架

先新建一个大类,里面要包含constructor这个构造函数(我们知道,在js当中这个函数是自动执行的)

class Tab {
    constructor() {
        // 获取元素(获取到文档元素--最大的那个页面)
        this.main = document.querySelector(id);
    }}

2. 获取到页面之后,搭建大的框架

//增
addTab(){}
//删
removeTab(){}
//切换
toggleTab(){}
//改
editTab(){
这个里面包含了双击修改文字内容的功能
}

3. 添加内容和功能

<main>
      <h4>
          Js 面向对象 动态添加标签页
      </h4>
      <div class="tabsbox" id="tab">
          <!-- tab 标签 -->
          <nav class="fisrstnav">
              <ul>
                  <li class="liactive"><span>测试1</span><span class="iconfont icon-guanbi"></span></li>
                  <li><span>测试2</span><span class="iconfont icon-guanbi"></span></li>
                  <li><span>测试3</span><span class="iconfont icon-guanbi"></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>

4. 实现tab栏切换改变内容

我们可以想到,想实现切换功能肯定会有点击事件发生,那我们首先肯定是获取元素并绑定事件。我们又可以想到不止这一个功能需要有点击事件,所以我们直接定义一个函数,谁需要谁直接调用

我们定义一个init函数,首先要获取元素(把页面主要框架写着方便梳理),看页面元素,我们需要盒子(#tabsbox,#liactive,#tabscon,#conactive)来实现功能。先获取,注意,这里我们应该写到constructor里面,因为我们需要一执行代码就获取到元素

// li的父元素
this.ul = this.main.querySelector(‘.fisrstnav ul:first-child’);
// section 父元素
this.fsection = this.main.querySelector(‘.tabscon’);
this.lis = this.main.querySelectorAll(‘li’);
this.sections = this.main.querySelectorAll(‘section’);
this.spans = this.main.querySelectorAll(‘.fisrstnav li span:first-child’);

获取到元素之后添加点击事件init(){}
我们想要知道是具体的哪一个选项卡要实现功能,就必须要知道该选项卡的索引号index。直接遍历li

init() {
            // init 初始化操作让相关的元素绑定事件
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].index = i;
                this.lis[i].onclick = this.toggleTab;
            }
        }
toggleTab() {
            this.className = 'liactive';//点击当前选项卡,获得当前的类名为liactive
            that.sections[this.index].className = 'conactive';//当前选项卡对应的底部内容
        }

写到这,还不够,因为这样只要点击过,所有的选项卡都会成为选中状态,我们需要利用排他思想,只让当前的选项卡为选中状态

// 清除所有li 和section 的类
    clearClass() {
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].className = '';
                this.sections[i].className = '';
            }
        }

然后让 toggleTab()调用这个类

添加一句代码,这里需要注意的是,应该写在最上面,让他最先执行
that.clearClass();
这里为什么用that稍后会指出

5. 接着在末尾增加新的tab栏

添加新的选项卡,我们可以想到给按钮添加点击事件,然后让选项卡追加内容
同样的先获取元素

this.add = this.main.querySelector(‘.tabadd’);

然后添加功能(在这里面比较牛B的是添加内容,不像之前还得一点一点写进去,直接运用一种新的办法insertAdjacentHTML,具体的使用方法可以去查一下)

addTab() {
            // (1) 创建li元素和section元素 
            var random = Math.random();//这个是无关紧要的,为了验证的
            var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>';
            var section = '<section class="conactive">测试 ' + random + '</section>';
            // (2) 把这两个元素追加到对应的父元素里面
            that.ul.insertAdjacentHTML('beforeend', li);
            that.fsection.insertAdjacentHTML('beforeend', section);
        }

这里又会出现新的问题了,添加之后,因为是后来添加的。所以获取不到他们的新的索引号,那我们就可以想到,动态添加元素,从而让大家都有索引号供我们调用

updateNode() {
            this.lis = this.main.querySelectorAll('li');
            this.sections = this.main.querySelectorAll('section');
            this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
        }

添加之后调用,在哪调用比较合适呢,我们可以想到让他获取index之前就动态获取元素,所以我们把它添加到init()里面,而且要在最上面,让他最先执行。
然后我们需要在addtab(){}中调用

that.init();

这里又需要注意啦,同样的会出现选定状态不变的情况,所以我们再次调用排他思想那个函数

that.clearClass();

6. 添加删除选项卡

实现删除功能,同理,先获取元素,在添加功能

this.remove = this.main.querySelectorAll(‘.icon-guanbi’);

而且我们需要把他写到动态添加函数里面,随时获取新的index
删除的思路是什么:获取事件之后,我们点击后要删除,获得索引号,那li 也没有索引号,我们便直接获取ul的,也就是他的父亲节点

removeTab(e) {
            e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件
            var index = this.parentNode.index;
            // 根据索引号删除对应的li 和section   remove()方法可以直接删除指定的元素
            that.lis[index].remove();
            that.sections[index].remove();
            that.init();
           
            
        }

我们写到这的时候,会发现,当你删除某个选项卡的时候,其选中状态会发生改变,我们需要设置一个条件语句,来解决这个问题

// 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变
if (document.querySelector(‘.liactive’)) return;
// 当我们删除了选中状态的这个li 的时候, 让它的前一个li 处于选定状态
index–;

现在又有新的问题产生了,当我们删除第一个之后index就不应该再减一了。所以我们需要设置条件,在这个项目中,老师利用的是短路特性来解决的这个问题

// 手动调用我们的点击事件 不需要鼠标触发
that.lis[index] && that.lis[index].click();

7. 双击修改选项卡内容

双击禁止选定文字(直接记住固定代码就好了

window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();

双击后获取到当前内容,然后给它添加一个能输入文字的文本框

var str = this.innerHTML;
this.innerHTML = '<input type="text" />'; var input = this.children[0];
input.value = str;

我们还要实现离开文本框之后,输入的内容传给span,添加离开事件,或者按下回车之后也要传回给span

// 当我们离开文本框就把文本框里面的值给span 
        input.onblur = function() {
            this.parentNode.innerHTML = this.value;
        };
        // 按下回车也可以把文本框里面的值给span
        input.onkeyup = function(e) {
            if (e.keyCode === 13) {
                // 手动调用表单失去焦点事件  不需要鼠标离开操作
                this.blur();
            }
        }

最后我们还要实现的功能是双击之后文字处于选定状态,可以直接一下子全部改变

input.select(); // 文本框里面的文字处于选定状态

最后的最后,我们还需要实例化,使整个代码调用起来

  • 还有一个遗留的小问题:为什么用that 这里that只是this的一个替代品。我们this指向的对象因为不同的调用对象是不同的,这个项目里面的that指代的是实例化的大的this。从而保证指向的对象是正确的。

3.完整代码

var that;
class Tab {
    constructor(id) {
        // 获取元素
        that = this;
        this.main = document.querySelector(id);
        this.add = this.main.querySelector('.tabadd');
        // li的父元素
        this.ul = this.main.querySelector('.fisrstnav ul:first-child');
        // section 父元素
        this.fsection = this.main.querySelector('.tabscon');
        this.init();
    }
    init() {
            this.updateNode();
            // init 初始化操作让相关的元素绑定事件
            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;

            }
        }
        // 因为我们动态添加元素 需要从新获取对应的元素
    updateNode() {
            this.lis = this.main.querySelectorAll('li');
            this.sections = this.main.querySelectorAll('section');
            this.remove = this.main.querySelectorAll('.icon-guanbi');
            this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
        }
        // 1. 切换功能
    toggleTab() {
            // console.log(this.index);
            that.clearClass();
            this.className = 'liactive';
            that.sections[this.index].className = 'conactive';
        }
        // 清除所有li 和section 的类
    clearClass() {
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].className = '';
                this.sections[i].className = '';
            }
        }
        // 2. 添加功能
    addTab() {
            that.clearClass();
            // (1) 创建li元素和section元素 
            var random = Math.random();
            var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>';
            var section = '<section class="conactive">测试 ' + random + '</section>';
            // (2) 把这两个元素追加到对应的父元素里面
            that.ul.insertAdjacentHTML('beforeend', li);
            that.fsection.insertAdjacentHTML('beforeend', section);
            that.init();
        }
        // 3. 删除功能
    removeTab(e) {
            e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件
            var index = this.parentNode.index;
            console.log(index);
            // 根据索引号删除对应的li 和section   remove()方法可以直接删除指定的元素
            that.lis[index].remove();
            that.sections[index].remove();
            that.init();
            // 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变
            if (document.querySelector('.liactive')) return;
            // 当我们删除了选中状态的这个li 的时候, 让它的前一个li 处于选定状态
            index--;
            // 手动调用我们的点击事件  不需要鼠标触发
            that.lis[index] && that.lis[index].click();
        }
        // 4. 修改功能
    editTab() {
        var str = this.innerHTML;
        // 双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        // alert(11);
        this.innerHTML = '<input type="text" />';
        var input = this.children[0];
        input.value = str;
        input.select(); // 文本框里面的文字处于选定状态
        // 当我们离开文本框就把文本框里面的值给span 
        input.onblur = function() {
            this.parentNode.innerHTML = this.value;
        };
        // 按下回车也可以把文本框里面的值给span
        input.onkeyup = function(e) {
            if (e.keyCode === 13) {
                // 手动调用表单失去焦点事件  不需要鼠标离开操作
                this.blur();
            }
        }
    }

}
new Tab('#tab');