昨天看了汪老师发的面向对象实现的选项卡,没有感觉到面向对象的特征,完全是把所有操作堆到了构造器,今天自己试试。
分析
本来想用状态模式呢,发现每个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