JavaScript高级-----2.面向对象(案例 Tab栏切换)

Posted deer_cen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript高级-----2.面向对象(案例 Tab栏切换)相关的知识,希望对你有一定的参考价值。

4. 面向对象案例



  • 第一步
class Tab {
    constructor(id) {
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
        this.init();//new的时候自动调用 用于测试代码console.log(this.index);是否可以打印正确的索引号
    }

    //初始化模块 让相关的元素绑定事件
    init() {
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].index = i;
            this.lis[i].onclick = function() {
                console.log(this.index);
            }
        }
    }

    //1. 切换功能
    toggleTab() {}

    //2. 添加功能
    addTab() {}

    //3. 删除功能
    removeTab() {}

    //4. 修改功能
    editTab() {}
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');
  • 第二步
class Tab {
    constructor(id) {
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
        this.init();
    }

    //初始化模块 让相关的元素绑定事件
    init() {
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].index = i;
            this.lis[i].onclick = this.toggleTab; //点击之后调用函数toggleTab(),函数前面的this就是函数的调用者。this.toggleTab的this就是被点击的li
        }
    }

    //1. 切换功能
    toggleTab() {
    //函数里面的this都指向函数的调用者
        console.log(this.index); //这个this指向当前的li,每个li都有一个index属性
    }

    //2. 添加功能
    addTab() {}

    //3. 删除功能
    removeTab() {}

    //4. 修改功能
    editTab() {}
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');
  • 第三步:切换功能的实现
var that;
class Tab {
    constructor(id) {
        that = this;
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
        this.init();
    }

    //初始化模块 让相关的元素绑定事件
    init() {
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].index = i;
            this.lis[i].onclick = this.toggleTab; //点击之后调用函数toggleTab()
        }
    }

    //1. 切换功能
    toggleTab() {
        // console.log(this.index); //这个this指向当前的li,每个li都有一个index属性
        that.clearClass(); //必须要实例对象来调用这个函数
        this.className = "liactive";
        that.sections[this.index].className = "conactive"; //that指向实例化的对象(大盒子)
    }

    //清除其他"类"的函数
    clearClass() { //由于这里是实例对象that来调用该函数,所以该函数里的this依然指向的是调用者:实例对象Tab
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = \'\';
            this.sections[i].className = \'\';
        }
    }

    //2. 添加功能
    addTab() {}

    //3. 删除功能
    removeTab() {}

    //4. 修改功能
    editTab() {}
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');

对于insertAdjacenthtml():https://developer.mozilla.org/zh-CN/docs/Web/API/Element/insertAdjacentHTML

若用appendChild,那么还要经过createElement创建。。。

  • 第四步
var that;
class Tab {
    constructor(id) {
        that = this;
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
        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(); //这里的this是实例对象,说明实例对象来调用的init(),那么init()里面的this都指向实例对象
    }

    //初始化模块 让相关的元素绑定事件
    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; //点击之后调用函数toggleTab()
        }
    }

    //1. 切换功能
    toggleTab() {
        // console.log(this.index); //这个this指向当前的li,每个li都有一个index属性
        that.clearClass(); //必须要实例对象来调用这个函数
        this.className = "liactive";
        that.sections[this.index].className = "conactive"; //that指向实例化的对象(大盒子)
    }

    //清除其他"类"的函数
    clearClass() { //由于这里是实例对象that来调用该函数,所以该函数里的this依然指向的是调用者:实例对象Tab
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = \'\';
            this.sections[i].className = \'\';
        }
    }

    //2. 添加功能
    addTab() {
        that.clearClass();
        //创建li和section
        var li = \'<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"></span></li>\';
        var section = \'<section class="conactive">测试1</section>\';
        //将li和section追加到父元素里
        that.ul.insertAdjacentHTML(\'beforeend\', li);
        that.fsection.insertAdjacentHTML(\'beforeend\', section);
    }

    //3. 删除功能
    removeTab() {}

    //4. 修改功能
    editTab() {}
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');

bug1:那些新增的li没有切换功能
解决思路:由于一部分li是后来才添加的,所以要求,当点击加号后,需要重新获取所有的小li和所有的section

var that;
class Tab {
    constructor(id) {
        that = this;
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        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(); //这里的this是实例对象,说明实例对象来调用的init(),那么init()里面的this都指向实例对象
    }

    //获取所有的li和section
    updateNode() { //解决bug1
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
    }

    //初始化模块 让相关的元素绑定事件
    init() {
        this.updateNode(); //解决bug1
        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; //点击之后调用函数toggleTab()
        }
    }

    //1. 切换功能
    toggleTab() {
        // console.log(this.index); //这个this指向当前的li,每个li都有一个index属性
        that.clearClass(); //必须要实例对象来调用这个函数
        this.className = "liactive";
        that.sections[this.index].className = "conactive"; //that指向实例化的对象(大盒子)
    }

    //清除其他"类"的函数
    clearClass() { //由于这里是实例对象that来调用该函数,所以该函数里的this依然指向的是调用者:实例对象Tab
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = \'\';
            this.sections[i].className = \'\';
        }
    }

    //2. 添加功能
    addTab() {
        that.clearClass();
        //创建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>\';
        //将li和section追加到父元素里
        that.ul.insertAdjacentHTML(\'beforeend\', li);
        that.fsection.insertAdjacentHTML(\'beforeend\', section);
        that.init(); //解决bug1   重新绑定事件
    }

    //3. 删除功能
    removeTab() {}

    //4. 修改功能
    editTab() {}
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');

var that;
class Tab {
    constructor(id) {
        that = this;
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        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(); //这里的this是实例对象,说明实例对象来调用的init(),那么init()里面的this都指向实例对象
    }

    //获取所有的li和section和❌
    updateNode() { //解决bug1
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
        //获取❌
        this.remove = this.main.querySelectorAll(\'.icon-guanbi\');
    }

    //初始化模块 让相关的元素绑定事件
    init() {
        this.updateNode(); //解决bug1
        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; //点击之后调用函数toggleTab()
            this.remove[i].onclick = this.removeTab;
        }
    }

    //1. 切换功能
    toggleTab() {
        // console.log(this.index); //这个this指向当前的li,每个li都有一个index属性
        that.clearClass(); //必须要实例对象来调用这个函数
        this.className = "liactive";
        that.sections[this.index].className = "conactive"; //that指向实例化的对象(大盒子)
    }

    //清除其他"类"的函数
    clearClass() { //由于这里是实例对象that来调用该函数,所以该函数里的this依然指向的是调用者:实例对象Tab
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = \'\';
            this.sections[i].className = \'\';
        }
    }

    //2. 添加功能
    addTab() {
        that.clearClass();
        //创建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>\';
        //将li和section追加到父元素里
        that.ul.insertAdjacentHTML(\'beforeend\', li);
        that.fsection.insertAdjacentHTML(\'beforeend\', section);
        that.init(); //解决bug1
    }

    //3. 删除功能
    removeTab(e) {
        e.stopPropagation(); //阻止点击事件冒泡,❌有点击事件,li也有点击事件(导致li切换)。若不阻止冒泡,点击❌的时候会导致li切换toggleTab() 
        var index = this.parentNode.index; //也就是得到 所在li的索引号
        console.log(index);
        //根据索引号删除对应的li和section   remove()可以直接删除指定的元素
        that.lis[index].remove();
        that.sections[index].remove();
        //删除之后也要重新获取一下当前的元素
        that.init();
    }

    //4. 修改功能
    editTab() {}
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');

现在进一步优化删除操作:希望将选中的li删除之后,程序自动将其前一个li看作选中的li,否则会出现没有选中li的情况,如下:

//3. 删除功能
removeTab(e) {
    e.stopPropagation(); //阻止点击事件冒泡,❌有点击事件,li也有点击事件(导致li切换)。若不阻止冒泡,点击❌的时候会导致li切换toggleTab() 
    var index = this.parentNode.index; //也就是得到 所在li的索引号
    console.log(index);
    //根据索引号删除对应的li和section   remove()可以直接删除指定的元素
    that.lis[index].remove();
    that.sections[index].remove();
    //删除之后也要重新获取一下当前的元素
    that.init();
    //当我们删除了选中状态这个li的时候,让它的前一个li处于选中状态
    index--;
    that.lis[index].click(); //让前一个小li自动做一个点击动作
}

进一步优化:若当前是第一个li,那么点击删除之后,index==-1,程序就会报错,解决办法如下:

//3. 删除功能
removeTab(e) {
    e.stopPropagation(); //阻止点击事件冒泡,❌有点击事件,li也有点击事件(导致li切换)。若不阻止冒泡,点击❌的时候会导致li切换toggleTab() 
    var index = this.parentNode.index; //也就是得到 所在li的索引号
    console.log(index);
    //根据索引号删除对应的li和section   remove()可以直接删除指定的元素
    that.lis[index].remove();
    that.sections[index].remove();
    //删除之后也要重新获取一下当前的元素
    that.init();
    //当我们删除了选中状态这个li的时候,让它的前一个li处于选中状态
    index--;
    that.lis[index] && that.lis[index].click(); //如果有这个li(即索引号大于等于0),就让前一个小li自动做一个点击动作
}

有出现一个问题:若选中的li是第4个,要删除的li是第2个,那么在删除2之后,选中的li自动更换为第1个,我们希望的是选中的li不会更换,这样更友好。

//3. 删除功能
removeTab(e) {
    e.stopPropagation(); //阻止点击事件冒泡,❌有点击事件,li也有点击事件(导致li切换)。若不阻止冒泡,点击❌的时候会导致li切换toggleTab() 
    var index = this.parentNode.index; //也就是得到 所在li的索引号
    console.log(index);
    //根据索引号删除对应的li和section   remove()可以直接删除指定的元素
    that.lis[index].remove();
    that.sections[index].remove();
    //删除之后也要重新获取一下当前的元素
    that.init();
    //当我们删除了不是选中状态这个li的时候,原来选中状态的li保持不变
    if (document.querySelector(".liactive")) return; //如果当前还有选中状态的li,就不执行以下代码( 若只有一个if可以不加{})
    //当我们删除了选中状态这个li的时候,让它的前一个li处于选中状态
    index--;
    that.lis[index] && that.lis[index].click(); //如果有这个li(即索引号大于等于0),就让前一个小li自动做一个点击动作
}

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

上述代码可以禁止双击时选中文字

var that;
class Tab {
    constructor(id) {
        that = this;
        //获取元素
        this.main = document.querySelector(id); //最大的Tab盒子
        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(); //这里的this是实例对象,说明实例对象来调用的init(),那么init()里面的this都指向实例对象
    }

    //获取所有的li和section和❌
    updateNode() { //解决bug1
        this.lis = this.main.querySelectorAll(\'li\'); //大盒子里面的li
        this.sections = this.main.querySelectorAll(\'section\');
        //获取❌
        this.remove = this.main.querySelectorAll(\'.icon-guanbi\');
        //获取span
        this.spans = this.main.querySelectorAll(\'.fisrstnav li span:first-child\');
    }

    //初始化模块 让相关的元素绑定事件
    init() {
        this.updateNode(); //解决bug1
        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; //点击之后调用函数toggleTab()
            this.remove[i].onclick = this.removeTab;
            this.spans[i].ondblclick = this.editTab;
        }
    }

    //1. 切换功能
    toggleTab() {
        // console.log(this.index); //这个this指向当前的li,每个li都有一个index属性
        that.clearClass(); //必须要实例对象来调用这个函数
        this.className = "liactive";
        that.sections[this.index].className = "conactive"; //that指向实例化的对象(大盒子)
    }

    //清除其他"类"的函数
    clearClass() { //由于这里是实例对象that来调用该函数,所以该函数里的this依然指向的是调用者:实例对象Tab
        for (var i = 0; i < this.lis.length; i++) {
            this.lis[i].className = \'\';
            this.sections[i].className = \'\';
        }
    }

    //2. 添加功能
    addTab() {
        that.clearClass();
        //创建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>\';
        //将li和section追加到父元素里
        that.ul.insertAdjacentHTML(\'beforeend\', li);
        that.fsection.insertAdjacentHTML(\'beforeend\', section);
        that.init(); //解决bug1
    }

    //3. 删除功能
    removeTab(e) {
        e.stopPropagation(); //阻止点击事件冒泡,❌有点击事件,li也有点击事件(导致li切换)。若不阻止冒泡,点击❌的时候会导致li切换toggleTab() 
        var index = this.parentNode.index; //也就是得到 所在li的索引号
        console.log(index);
        //根据索引号删除对应的li和section   remove()可以直接删除指定的元素
        that.lis[index].remove();
        that.sections[index].remove();
        //删除之后也要重新获取一下当前的元素
        that.init();
        //当我们删除了不是选中状态这个li的时候,原来选中状态的li保持不变
        if (document.querySelector(".liactive")) return; //如果当前还有选中状态的li,就不执行以下代码( 若只有一个if可以不加{})
        //当我们删除了选中状态这个li的时候,让它的前一个li处于选中状态
        index--;
        that.lis[index] && that.lis[index].click(); //如果有这个li(即索引号大于等于0),就让前一个小li自动做一个点击动作
    }

    //4. 修改功能
    editTab() {
        //双击禁止选定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.section.empty();
        //双击生成文本框
        this.innerHTML = \'<input type="text" />\'; //这个this指向当前被点击的span
    }
}

//页面中可能会有很多Tab都需要使用这个功能,那就可以实例化不同的对象。
var tab = new Tab(\'#tab\');

完善

//4. 修改功能
editTab() {
    //这里的this指向当前被点击的span
    //先获取span中原先的文字
    var str = this.innerHTML;
    //双击禁止选定文字
    window.getSelection ? window.getSelection().removeAllRanges() : document.section.empty();
    //双击生成文本框
    this.innerHTML = \'<input type="text" />\';
    //获取文本框
    var input = this.children[0];
    //将原先span中的内容赋值给文本框
    input.value = str;
    //让文本框里的默认文字处于选定状态,这样用户可以直接删除或修改原先的内容,不需要一个字一个字地删
    input.select();
    //当文本框失去焦点,就将文本框里的值给span
    input.onblur = function() {
        //这里的this指向input
        this.parentNode.innerHTML = this.value; //this.parentNode即input的父亲即span
    }
}

继续优化:希望按下回车键,也可以将input内容给span

//4. 修改功能
editTab() {
    //这里的this指向当前被点击的span
    //先获取span中原先的文字
    var str = this.innerHTML;
    //双击禁止选定文字
    window.getSelection ? window.getSelection().removeAllRanges() : document.section.empty();
    //双击生成文本框
    this.innerHTML = \'<input type="text" />\';
    //获取文本框
    var input = this.children[0];
    //将原先span中的内容赋值给文本框
    input.value = str;
    //让文本框里的默认文字处于选定状态,这样用户可以直接删除或修改原先的内容,不需要一个字一个字地删
    input.select();
    //当文本框失去焦点,就将文本框里的值给span
    input.onblur = function() {
            //这里的this指向input
            this.parentNode.innerHTML = this.value; //this.parentNode即input的父亲即span
        }
        // 按下回车键,也可以将input内容给span  
    input.onkeyup = function(e) {
        //这里的this指向input
        if (e.keyCode === 13) { //回车键的ASCII是13
            this.blur(); //手动调用失去焦点事件  不用加on
        }
    }
}

下面继续写对section修改内容

//初始化模块 让相关的元素绑定事件
    init() {
        this.updateNode(); //解决bug1
        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; //点击之后调用函数toggleTab()
            this.remove[i].onclick = this.removeTab;
            this.spans[i].ondblclick = this.editTab;
            this.sections[i].ondblclick = this.editTab;
        }
    }

以上是关于JavaScript高级-----2.面向对象(案例 Tab栏切换)的主要内容,如果未能解决你的问题,请参考以下文章

原型和原型链 —javascript面向对象高级

JavaScript高级 面向对象(13)--构造函数的执行过程

继承性—javascript面向对象高级

JS高级进阶JavaScript初识面向对象

JavaScript高级 面向对象的程序设计

JavaScript高级程序设计笔记之面向对象