Documents: Tabs
Source: tiny – wheels
Click Star if you think it works.
The effect
Train of thought
The difficulty of this component is to control the movement of the bottom bar of each TAB item and the movement of the corresponding panel. The most common way is to change the position of the element through the transform, without nonsense, directly into the code
implementation
The article lists only the key parts of the code, leaving the rest of the logic to explore the source code in the project
The Tabs component needs to be customized by users, so some configuration is implemented through the HTML custom attributes (the compatibility of custom tags is not good at present, so it is not considered for now). The HTML structure of the component is as follows:
<div class="tabs" data-tab-active="2" data-tab-disabled="3">
<div data-tab-name="TAB 1" data-tab-key="1">The content of 1</div>
<div data-tab-name="TAB 2" data-tab-key="2">Content of the 2</div>
<div data-tab-name="TAB 3" data-tab-key="3">The content of 3</div>
<div data-tab-name="TAB 4" data-tab-key="4">The content of 4</div>
</div>
Copy the code
The specific usage of each attribute is documented, so I won’t repeat it here
Component structure rendering source code already exists, the final render HTML structure is like this:
<div class="tabs tiny-tabs" data-tab-active="2" data-tab-disabled="3">
<div class="tab-header">
<span class="tab-item">TAB 1</span>
<span class="tab-item active">TAB 2</span>
<span class="tab-item disabled">TAB. 3</span>
<span class="tab-item">TAB 4</span>
<span
class="tab-line"
style="width: 46px; transform: translateX(77px);"
></span>
</div>
<div class="tab-panels animated" style="transform: translateX(-100%);">
<div data-tab-name="TAB 1" data-tab-key="1" class="tab-panel">The content of 1</div>
<div data-tab-name="TAB 2" data-tab-key="2" class="tab-panel active">Content of the 2</div>
<div data-tab-name="TAB 3" data-tab-key="3" class="tab-panel">The content of 3</div>
<div data-tab-name="TAB 4" data-tab-key="4" class="tab-panel">The content of 4</div>
</div>
</div>
Copy the code
We can render the corresponding number of tab-items according to the content of the TAB set by the user. The position and width of the tab-item can be calculated by offsetLeft and offsetWidth, and then change the corresponding style:
setTabs () {
this.? tabItems =this.$container.querySelectorAll('.tab-item')
this.$tabLine = this.$container.querySelector('.tab-line')
this.setTabStatus()
const tabIndex = this.getTabIndex() ? this.getTabIndex() : 0
if (this.? tabItems[tabIndex]) {const { offsetWidth, offsetLeft } = this.? tabItems[tabIndex]this.setTabItem(this.? tabItems[tabIndex])this.setTabLine(offsetWidth, offsetLeft)
this.setTabPanel(this.? tabPanels[tabIndex], tabIndex) } } setTabLine (width, left) {this.$tabLine.style.width = `${width}px`
this.$tabLine.style.transform = `translateX(${left}px)`
}
Copy the code
Tab-panel Settings are the same:
setTabPanel ($panel, index) {
this.$tabPanelContainer.style.transform = `translateX(-${index * 100}%) `
this.? tabPanels.forEach($panel= > $panel.classList.remove('active'))
$panel.classList.add('active')
setTimeout((a)= > {
if (this.options.animated) {
this.$tabPanelContainer.classList.add('animated')}}}Copy the code
Note that we do not want the tab-Panel to slide when we first load the component, so we need to use setTimeout to delay loading the Transition animation style
So much for the core logic of the Tabs component. What is left is some implementation of configuring properties and event binding:
getTabIndex () {
const tabKey = this.$container.dataset.tabActive
let tabIndex = tabKey
if (tabKey) {
this.? tabPanels.forEach(($panel, index) = > {
if ($panel.dataset.tabKey === tabKey) {
tabIndex = index
}
})
}
return tabIndex
}
setTabStatus () {
const tabKey = this.$container.dataset.tabDisabled
if (tabKey) {
this.? tabPanels.forEach(($panel, index) = > {
if ($panel.dataset.tabKey === tabKey) {
this.? tabItems[index].classList.add('disabled')
}
})
}
}
bindTabs () {
this.? tabItems.forEach($tab= > {
$tab.addEventListener('click', () = > {if(! $tab.classList.contains('disabled')) {
const index = [...this.?tabItems].indexOf($tab)
this.setTabItem($tab)
this.setTabLine($tab.offsetWidth, $tab.offsetLeft)
this.setTabPanel(this.? tabPanels[index], index)this.options.callback.call(null, $tab, this.? tabPanels[index].dataset.tabKey) } }) }) }Copy the code
Tab-active (initial activation item) and Tab-disabled (initial disablement item) get the corresponding attribute value through the API of the dataset, and then iterate to find the item to be set. To bind an event, you need to pass a reference to the current element, tab-key, and other parameters to the callback function
This completes the basic functionality of the Tabs component. Of course, some more complex functions can be implemented as well: Add and remove tabs, responsive tab-items, card-style tabs, etc. These functions are implemented in Element-UI, iView, and Ant-Design, and can be extended by referring to their effects
Use Vue or React to encapsulate components like this, but only to eliminate DOM manipulation and simplify the configuration of component properties. Some of the internal core implementation principles are common, such as writing component structures using Vue.
<template>
<tabs :active.sync="activeTab" @update:selected="callback">
<tabs-head>
<tabs-item name="one" disabled>TAB 1</gabs-item>
<tabs-item name="two" active>TAB 2</tabs-item>
<tabs-item name="three">TAB. 3</tabs-item>
<tabs-head>
<tabs-body>
<tabs-panel name="one">The content of 1</tabs-panel>
<tabs-panel name="two">Content of the 2</tabs-panel>
<tabs-panel name="three">The content of 3</tabs-panel>
</tabs-body>
<tabs>
</template>
Copy the code
As you can see, the configuration of the attributes is much simpler, and the component structure is similar to what we rendered with native HTML (in fact, we can simulate this effect with native custom tags, but browser compatibility is not good at present). The tabs component is still available in Vue. The tabs component is designed to calculate the offsetWidth, offsetLeft and other attributes
To be continued…