LuLu UI Edge版中文文档 » Tab选项卡切换

教程

Tab切换

安装与调用

引用下面CSS文件:

<link rel="stylesheet" href="https://unpkg.com/lu2/theme/edge/css/common/ui/Tab.css">

引用下面的JS文件:

<script type="module" src="https://unpkg.com/lu2/theme/edge/js/common/ui/Tab.js"></script>

支持es6 import的使用方式:

<script type="module">
    import 'https://unpkg.com/lu2/theme/edge/js/common/ui/Tab.js';
</script>
import 'lu2/theme/edge/css/common/ui/Tab.css';
import 'lu2/theme/edge/js/common/ui/Tab.js';

如果 JS import语法报错,试试在业务代码中动态引入。

import('lu2/theme/edge/js/common/ui/Tab.js');

概要

这里的Tab切换本质上是一个切换器,任意1对1的交互效果都支持,例如展开收起、选项卡切换,又或者文档后面演示的轮播效果。

<ui-tab>自定义元素

<ui-tab>自定义元素可以实现几乎所有常见的切换效果。

实现原理如下:

  1. <ui-tab>元素点击(或悬停)的时候,判断自身是否有name属性:
    • 如果有name属性,则同name属性值的全部<ui-tab>元素仅当前点击的<ui-tab>元素保持open状态;
    • 如果没有name属性,则认为是独立的,表现为当前<ui-tab>元素的open状态在truefalse之间不断切换。
  2. 通过target属性匹配对应的面板元素,根据<ui-tab>元素的open状态添加或移除面板元素的.active类名。

下面通过多个不同类型的案例了解<ui-tab>自定义元素的功能表现。

1. 展开与收起

点击下面的“更多”。

  • 列表1
  • 列表2
  • 列表3
  • 更多↓

相关代码如下所示:

<ul id="c1">
    <li>列表1</li>
    <li>列表2</li>
    <li>列表3</li>
    <li hidden>列表4</li>
    <li hidden>列表5</li>
    <li hidden>列表6</li>
    <ui-tab target="c1">更多↓</ui-tab>
</ul>
ul.active [hidden] {
    display: list-item;
}
ul.active ui-tab {
    -webkit-text-fill-color: transparent;
}
ul.active ui-tab::before {
    position: absolute;
    content: '收起↑';
    -webkit-text-fill-color: currentColor;
}

在本例中,“更多↓”所在的<ui-tab>元素没有name属性值,属于A/B切换效果,因此,随着点击行为的触发,当前<ui-tab>元素会不断切换open属性;同时target属性所匹配的元素'c1'也会不断切换类名'active'

配合相关的CSS就可以控制元素的显隐了。

2. 手风琴效果

左侧列表没有name属性,每个标题展开收起都是独立的;右侧列表有name属性,标题展开与收起是联动的。

标题A
内容1
内容2
标题B
内容1
内容2
标题C
内容1
内容2
标题A
内容1
内容2
标题B
内容1
内容2
标题C
内容1
内容2

HTML代码如下:

<div class="accordion">
<dl>
    <dt id="dt1"><ui-tab target="dt1">标题A</ui-tab></dt>
    <dd>内容1</dd>
    <dd>内容2</dd>
    <dt id="dt2"><ui-tab target="dt2">标题B</ui-tab></dt>
    <dd>内容1</dd>
    <dd>内容2</dd>
    <dt id="dt3"><ui-tab target="dt3">标题C</ui-tab></dt>
    <dd>内容1</dd>
    <dd>内容2</dd>
</dl>

<dl>
    <dt id="dt4"><ui-tab target="dt4" name="dt">标题A</ui-tab></dt>
    <dd>内容1</dd>
    <dd>内容2</dd>
    <dt id="dt5"><ui-tab target="dt5" name="dt">标题B</ui-tab></dt>
    <dd>内容1</dd>
    <dd>内容2</dd>
    <dt id="dt6"><ui-tab target="dt6" name="dt">标题C</ui-tab></dt>
    <dd>内容1</dd>
    <dd>内容2</dd>
</dl>
</div>

关键CSS如下:

.accordion dd {
display: none;
}
.accordion .active + dd,
.accordion .active + dd + dd {
display: block;
}

3. 选项卡效果

选项卡1 选项卡2 选项卡3

我是选项卡1对应的图片

我是选项卡2对应的图片

我是选项卡3对应的图片

主要看下HTML代码:

<div class="tab-x">
    <div class="tab">
        <ui-tab class="tab-label" name="s-tab" target="t1" open>选项卡1</ui-tab>
        <ui-tab class="tab-label" name="s-tab" target="t2">选项卡2</ui-tab>
        <ui-tab class="tab-label" name="s-tab" target="t3">选项卡3</ui-tab>
    </div>
    <div id="t1" class="tab-content active">
        <p>我是选项卡1对应的图片</p>
        <img src="1.jpg" />
    </div>
    <div id="t2" class="tab-content">
        <p>我是选项卡2对应的图片</p>
        <img src="2.jpg" />
    </div>
    <div id="t3" class="tab-content">
        <p>我是选项卡3对应的图片</p>
        <img src="3.jpg" />
    </div>
</div>

本组件内置了一套选项卡UI样式,效果如下所示:

选项卡文档 业务JS分离演示

点击上方的选项卡按钮,可以看到切换效果。

相关的HTML样式和结构如下所示:

<div class="ui-tab-tabs">
    <ui-tab target="tabTarget1" name="tabgroup" class="ui-tab-tab" open>选项卡文档</ui-tab>
    <ui-tab target="tabTarget2" name="tabgroup" class="ui-tab-tab">业务JS分离演示</ui-tab>
</div>
<div class="ui-tab-contents">
    <div id="tabTarget1" class="ui-tab-content active"></div>
    <div id="tabTarget2" class="ui-tab-content"></div>
</div>

其中的类名ui-tab-tabsui-tab-tabui-tab-contentsui-tab-content对应的样式与标签无关,因此,如果当前选项卡就是普通的链接跳转,替换成<a>元素即可。

<div class="ui-tab-tabs">
    <a href="/a/" class="ui-tab-tab" open>选项卡1</a>
    <a href="/b/" class="ui-tab-tab">选项卡2</a>
</div>

每次进来,图片会重载一次,高度随机。演示选项卡切换事件是如何触发的。

代码如下:

// 点击第2个选项卡
// 这里'show'事件也可以换成'click'事件
document.querySelector('ui-tab[target="tabTarget2"]').addEventListener('show', function () {
    eleImgX.innerHTML = '<img src="some.jpg" height="' + Math.round(100 + 100 * Math.random()) + '">';
});

is-tab属性

本组件还支持在普通HTML元素上使用应用切换效果,方法是添加is-tab属性。

切换对象使用data-target属性指定,对于<a>元素,也可以使用href属性指定(此时需要前面加'#')。

例如上面提到的展开收起效果也可以使用如下所示的HTML结构:

<ul id="c2">
    <li>列表1</li>
    <li>列表2</li>
    <li>列表3</li>
    <li hidden>列表4</li>
    <li hidden>列表5</li>
    <li hidden>列表6</li>
    <a href="#c2" class="blue" is-tab>更多↓</a>
    <!-- 或者 -->
    <!-- <a href="javascript:" class="blue" is-tab="c2"></a> -->
</ul>

实时效果如下:

  • 列表1
  • 列表2
  • 列表3
  • 更多↓

亦或者选项卡效果(同样使用name属性进行分组),这里使用<button>元素示意下:

<div class="ui-tab-tabs">
    <button data-target="p1" name="tab-button" class="ui-tab-tab" is-tab open>选项卡1</button>
    <button data-target="p2" name="tab-button" class="ui-tab-tab" is-tab>选项卡2</button>
</div>
<div id="p1" class="ui-tab-content active">
    <p>我是选项卡1对应的图片</p>
    <img src="1.jpg" />
</div>
<div id="p2" class="ui-tab-content">
    <p>我是选项卡2对应的图片</p>
    <img src="2.jpg" />
</div>

我是选项卡1对应的图片

我是选项卡2对应的图片

设置了is-tab属性的DOM元素的切换效果底层还是借助<ui-tab>元素实现的,该<ui-tab>元素可以使用下面的语法获得:

dom['ui-tab'];   // 返回对应的<ui-tab>元素

另外,is-tab="prev"is-tab="next"元素在点击的时候,可以触发选项卡的上一项和下一项的切换效果,前提是使用data-name属性指向选项卡元素的name属性值,本文档最后的扩展效果有使用示意。

NodeList.tab()方法

本组件还支持手动绑定切换行为,同样针对普通元素元素。假设有如下所示的HTML代码:

<div id="myTabs" class="ui-tab-tabs">
    <button data-target="p3" class="ui-tab-tab" open>选项卡1</button>
    <button data-target="p4" class="ui-tab-tab">选项卡2</button>
</div>
<div id="p3" class="ui-tab-content active">...</div>
<div id="p4" class="ui-tab-content">...</div>

则执行下面的JavaScript代码就可以有选项卡切换效果了:

document.querySelectorAll('#myTabs button').tab();

我是选项卡1对应的图片

我是选项卡2对应的图片

语法和参数

对于<ui-tab>自定义元素,支持下面这些自定义参数。

参数名称 支持类型 默认值 释义
target String '' 可选。表示切换效果对应的元素的id值。
name String '' 可选。表示分组名。如果不设置,或者为空,则表示当前切换是不断循环的A/B切换效果。
eventType String 'click' 可选。表示事件类型。默认为点击事件。还支持'mouseover''mouseenter'等事件。值'hover'会按照'mouseenter'事件处理。
history Boolean false 可选。表示是否通过给url添加查询字符串记录选项卡的选中状态。默认值为false表示切换时候查询地址无变化。此参数生效需要name值必须存在。
autoplay Integer 3000 可选。表示是否自动播放。如果不设置,表示不自动播放。如果设置,则只要参数值不是整数,均采用默认的自动播放时间3000ms。autoplay自动播放时间是独立的,不同选项卡按钮可以有不同的自动播放时间。

history参数效果示意

鼠标经过下面的选项卡,同时观察URL地址的变化。

选项卡1 选项卡2

我是选项卡1对应的图片

我是选项卡2对应的图片

相关HTML如下所示:

<div class="ui-tab-tabs">
    <ui-tab target="tt1" name="rel" class="ui-tab-tab" history="true" eventtype="hover" open>选项卡1</ui-tab>
    <ui-tab target="tt2" name="rel" class="ui-tab-tab" history="true" eventtype="hover">选项卡2</ui-tab>
</div>
<div id="tt1" class="ui-tab-content active">...</div>
<div id="tt2" class="ui-tab-content">...</div>

可以看到name属性作为了查询字符串的key值,target属性值作为了查询字符串的value值。

虽然页面刷新的时候,选项卡会自动定位到上一个选中的选项卡元素上,但是这个过程是异步后执行的,用户会看到选项卡变化的过程,体验并不一定完美,因此建议页面直出的时候直接根据URL查询内容进行open属性和active类名的设置。

普通元素中的参数设置

普通元素中的参数设置与<ui-tab>自定义元素类似,区别在于需要在前面添加data-前缀(name参数除外),以及注意保持小写。

例如:

<button data-target="someId" data-history="true" data-eventtype="hover" is-tab open>选项卡1</button>

其中,对于<a>元素,可以使用href属性代替data-target,不过需要在前面添加一个井号#,例如:

<a href="#c2" class="blue" is-tab>更多↓</a>

Nodelist.tab()的语法和参数

语法如下:

Nodelist.tab(options);

其中options是可选参数,支持这3个参数:nameeventTypehistoryautoplay

例如下面这个效果:

内容1内容2

相关HTML和JS代码使用示意:

<button data-target="t-1">经过我-1</button>
<button data-target="t-2">经过我-2</button>
<p>
    <mark id="t-1" class="ui-tab-content">内容1</mark>
    <mark id="t-2" class="ui-tab-content">内容2</mark>
</p>
document.querySelectorAll('button[data-target^="t-"]').tab({
    name: 'free',
    history: true,
    eventType: 'hover'
});

new Tab()的语法和参数

对于单个普通HTML元素,也可以使用new Tab()语法进行构造。

new Tab()本质上是创建<ui-tab>自定义元素的快捷语法,底层通过for属性机制和普通HTML产生状态关联。

语法如下:

let eleTab = new Tab(trigger, options);

其中:

trigger
必需。String|Element。表示切换按钮元素。如果是字符串类型,则表示切换按钮元素。
options
可选。Object。表示可选参数,仅支持eventTypehistoryautoplay的设置。

事件与回调

仅在<ui-tab>元素中有自定义的事件支持,包括'show''hide''switch'这3个自定义事件。

其中:

show
切换显示的时候触发。使用示意:
tab.addEventListener('show', _ => {});
hide
切换隐藏的时候触发。使用示意:
tab.addEventListener('hide', _ => {});
switch
切换状态变化的时候触发。使用示意:
tab.addEventListener('switch', event => {
    console.log(this.open);
});

对于普通元素的切换,可以使用原生事件进行处理,例如,点击某个选项卡之后你希望做些事情,可以:

tab.addEventListener('click', _ => {});

扩展:广告图轮播实现

Tab组件本质上是个切换器,因此,也是可以用来实现轮播图效果的,使用autoplay参数即可触发自动播放:

样式需要重写,LuLu UI不提供,源码示意:

CSS部分:

.slide-container {
    position: relative;
    width: 350px; height: 105px;
    border: 1px solid #ddd;
}
.slide-a {
    position: absolute;
    transition: opacity .2s;
}
.slide-a:not(.active) {
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s, visibility .01s .2s;
}
.slide-img {
    display: block;
    width: 100%; height: 105px;
}
.slide-dot-x {
    width: 350px;
    text-align: center;
    position: relative;
    margin-top: -25px;
}
.slide-dot {
    display: inline-block;
    width: 10px; height: 10px;
    border: 4px solid transparent;
    background-color: #fff;
    background-clip: content-box;
    border-radius: 50%;
}
.slide-dot[open] {
    background-color: #cd0000;
}
.slide-prev,
.slide-next {
    position: absolute;
    width: 24px; height: 24px;
    border: 2px solid #fff;
    color: #fff;
    border-radius: 100%;
    top: -40px;
    display: grid;
    place-items: center;
    font-size: 14px;
    font-family: system-ui;
    cursor: pointer;
    background-color: #fff1;
}
.slide-prev {
    left: 20px;
}
.slide-next {
    right: 20px;
}

HTML部分:

<div id="slideContainer" class="slide-container">
    <a href id="slideLi1" class="slide-a active">
        <img src="1.jpg" class="slide-img">
    </a>
    <a href id="slideLi2" class="slide-a">
        <img src="2.jpg" class="slide-img">
    </a>
    <a href id="slideLi3" class="slide-a">
        <img src="3.jpg" class="slide-img">
    </a>
</div>
<div id="slideDot" class="slide-dot-x">
    <ui-tab eventtype="mouseover" name="sliderDot" class="slide-dot" autoplay open target="slideLi1"></ui-tab>
    <ui-tab eventtype="mouseover" name="sliderDot" class="slide-dot" autoplay="4000" target="slideLi2"></ui-tab>
    <ui-tab eventtype="mouseover" name="sliderDot" class="slide-dot" autoplay="5000" target="slideLi3"></ui-tab>
    <!-- 前后切换 -->
    <span class="slide-prev" is-tab="prev" data-name="sliderDot">◀</span>
    <span class="slide-next" is-tab="next" data-name="sliderDot">▶</span>
</div>

3个广告图自动播放时间分别是3s,4s和5s。

本页贡献者:

zhangxinxu,nanaSun