抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

昨天看了汪老师发的面向对象实现的选项卡,没有感觉到面向对象的特征,完全是把所有操作堆到了构造器,今天自己试试。

分析

本来想用状态模式呢,发现每个State的操作都是一样的——将所有的tab隐藏,显示当前的tab;然后因为你不知道下一次会转向哪种状态,用状态机写不合适。

思路:

传入bar和tab,对每个bar和tab分别标上index,通过index匹配他们

记录上一次点击元素的下标fromIndex,每次切换tab时,取出当前元素下标toIndex。对上一次点击的元素移出active,对当前元素index添加active。

所以我也是没能用面向对象的方法写出来,大体上跟将所有代码写在构造器里的操作差不多吧。

但是每次敲代码都会有进步,我记录下来现在的代码,以后再来看看。

代码

HTML结构:

<div class="tab">
    <ul class="tab-bar">
        <li class="bar">tab-1</li>
        <li class="bar">tab-2</li>
        <li class="bar">tab-3</li>
    </ul>
    <div class="tab-content">
        <div class="content">tab-1-content</div>
        <div class="content">tab-2-content</div>
        <div class="content">tab-3-content</div>
    </div>
</div>

CSS就不贴了,完整代码附在文章底部了。

JavaScript:

import EventEmitter from "events"

// 传入Tabs构造器:Option类型
interface Option {
    nav: string,
    panel: string,
    activeIndex?: number
}

// 自己手写了个选择题,勉强用
const $ = (selector: string) => 
	Array.prototype.slice.call(
    document.querySelectorAll(selector)
  ) as Array<HTMLElement>;


class Tabs {
    private readonly options: Option;
    private panels: Array<HTMLElement>;
    private navs: Array<HTMLElement>;
    private fromTab: number;
    event: EventEmitter;

    static defaultOptions = {
        activeIndex: 0
    }

    constructor(opt: Option) {
        this.options = Object.assign(Tabs.defaultOptions, opt);
        this.fromTab = this.options.activeIndex;
        // 向外开放切换事件
        this.event = new EventEmitter();

        this.init();
        this.bindEvent()
        this.switchTo(this.fromTab);
    }

    // 给节点加上标记,方便操作
    private init() {
        this.panels = $(this.options.panel)
        this.navs = $(this.options.nav)
        this.navs.forEach((elem, index) => {
            elem.dataset.index = String(index);
        })
        this.panels.forEach((elem, index) => {
            elem.dataset.index = String(index);
        })
    }

    // 给nav绑定点击事件
    private bindEvent() {
        this.navs.forEach(elem => {
            elem.onclick = () => {
                this.switchTo(+elem.dataset.index);
            }
        })
    }

    // 切换nav操作
    private switchNav(index: number) {
        // 记录上一次点击的nav下标,将其移出active,下同
        this.navs[this.fromTab]
          .classList.remove("active")
        this.navs[index].classList.add("active")
    }

    // 切换panel操作
    private switchPanel(index: number) {
        this.panels[this.fromTab]
          .classList.remove("active");
        this.panels[index].classList.add("active");
    }

    // 切换tab
    private switchTo(toIndex: number) {
        this.switchNav(toIndex);
        this.switchPanel(toIndex);
        this.event.emit("change", {
            toIndex, fromIndex: this.fromTab
        })
        // 更新上一次点击的下标
        this.fromTab = toIndex;
    }
}

const tab = new Tabs({
    nav: ".tab-bar .bar",
    panel: ".tab-content .content"
})
tab.event.on("change", (obj) => {
    console.log(obj)
})

完整代码:

MyCodebak/Web/sigle-page/oop-tabbar at master · sunzehui/MyCodebak (github.com)

附汪老师发的视频:

2021web前端-JavaScript-JavaScript面向对象 2- 9_面向对象——选项卡_哔哩哔哩_bilibili

评论