选项卡在 web 中的应用可以说是无处不在。因此掌握它的实现对我们来说非常有用。
通常情况下,选项卡由两部分组成。一部分是头部,它包含一堆按钮,每一个按钮对应不同的页面,按钮包括选中,与无法选中两种状态。另一部分则由一堆具体的页面组成。当我们点击按钮时,切换到对应的页面。
如果每个页面中包含的是根据动态加载的数据渲染出来的界面,那么通常只会有一个页面,点击按钮时重新加载数据并重新渲染页面。
我们先在 html 中将这两部分代码写出来。
10<div class="box" id="tab_wrap">20<ul class="tab_options">30<li data-index="0" class="item active">tab1</li>40<li data-index="1" class="item">tab2</li>50<li data-index="2" class="item">tab3</li>60<li data-index="3" class="item">tab4</li>70</ul>80<div class="tab_content">90<div class="item_box active">tab box 1</div>10<div class="item_box">tab box 2</div>11<div class="item_box">tab box 3</div>12<div class="item_box">tab box 4</div>13</div>14</div>
并简单写一些css代码。
10body {20margin: 0;30}4050ul, li {60list-style: none;70padding: 0;80}9010#tab_wrap {11max-width: 400px;12margin: 10px auto;13background: #efefef;14}1516#tab_wrap .tab_options {17height: 40px;18display: flex;19justify-content: space-around;20border-bottom: 1px solid #ccc;21}22#tab_wrap .tab_options li {23line-height: 40px;24cursor: pointer;25}26#tab_wrap .tab_options li.active {27color: red;28border-bottom: 1px solid red;29}3031#tab_wrap .tab_content {32min-height: 400px;33position: relative;34}3536#tab_wrap .tab_content .item_box {37position: absolute;38left: 0;39top: 0;40width: 100%;41display: none;42text-align: center;43}44#tab_wrap .tab_content .item_box.active {45display: block;46}
选项卡的实现原理非常简单。大家可以注意我们在 html 代码中,每一个头部按钮都保存了一个 data-index
属性。这个属性告诉我们这是第几个按钮。这个值同时也对应第几页。因此我们只需要声明一个 index 变量来保存当前页的序列,并在点击时把当前页的值修改为data-index
的值就可以了,与此同时,让当前按钮修改为选中状态,其他按钮修改为未选中状态,让当前页显示,其他页隐藏即可。JavaScript 代码如下。
10var tabHeader = document.querySelector('.tab_options');20var items = tabHeader.children;30var tabContent = document.querySelector('.tab_content');40var itemboxes = tabContent.children;5060var index = 0;7080tabHeader.addEventListener('click', function(e) {90var a = [].slice.call(e.target.classList).indexOf('item');10if (a > -1 && index != e.target.dataset.index) {11items[index].classList.remove('active');12itemboxes[index].classList.remove('active');13index = e.target.dataset.index;14items[index].classList.add('active');15itemboxes[index].classList.add('active');16}17}, false);
但是此时假设我们要新增一个功能,在html中新增两个按钮,点击他们我们可以分别切换到上一页跟下一页,我们应该怎么办?
1<button class="next">Next</button>2<button class="prev">Prev</button>
为了能够更直观的实现这个功能,我们尝试来将选项卡封装成为一个对象。代码如下:
10!function(ROOT) {20var index = 0;30function Tab(elem) {40this.tabHeader = elem.firstElementChild;50this.items = this.tabHeader.children;60this.tabContent = elem.lastElementChild;70this.itemboxes = this.tabContent.children;80this.max = this.items.length - 1;9010this.init();11}1213Tab.prototype = {14constructor: Tab,15init: function() {16this.tabHeader.addEventListener('click', this.clickHandler.bind(this), false);17},18clickHandler: function(e) {19var a = [].slice.call(e.target.classList).indexOf('item');20if (a > -1) {21this.switchTo(e.target.dataset.index);22}23},24switchTo: function(i) {25if (i == index) {26return;27}28this.items[index].classList.remove('active');29this.itemboxes[index].classList.remove('active');30index = i;31this.items[index].classList.add('active');32this.itemboxes[index].classList.add('active');33},34next: function() {35var tgIndex = 0;36if (index >= this.max) {37tgIndex = 0;38} else {39tgIndex = index + 1;40}41this.switchTo(tgIndex);42},43pre: function() {44var tgIndex = 0;45if (index == 0) {46tgIndex = this.max;47} else {48tgIndex = index - 1;49}50this.switchTo(tgIndex);51},52getIndex: function() {53return index;54}55}5657ROOT.Tab = Tab;58}(window);
在上面的代码中,我们将切换功能封装成为了基础的switchTo
方法,它接收一个表示页面序列的参数,只要我们调用这个方法,就能够切换到对应的页面。
因此基于这个基础方法,我们就能够很简单的扩展出下一页 next 与上一页 pre 方法。
那么再使用时,就很简单了,代码如下:
10var tab = new Tab(document.querySelector('#tab_wrap'));2030document.querySelector('.next').addEventListener('click', function() {40tab.next();50console.log(tab.getIndex());60}, false);7080document.querySelector('.prev').addEventListener('click', function() {90tab.pre();10console.log(tab.getIndex());11}, false);
这样,一个简单的选项卡功能就实现了。并且拥有了一些简单的扩展功能。从使用中我们可以看到,封装好之后,想要实现什么功能,我们只需要调用对应的接口即可。面向对象封装之后我们的代码变得非常简单直观。
需要注意的是,为了考验大家对于闭包的理解,在上面的代码中,我故意留了一个坑,大家只要在页面中创建两个选项卡,就能够发现问题。建议大家结合闭包思考一下为什么会出现这样的情况。