本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1. 开发目标
大型门户网站为了吸引尽可能多的用户,所涉及的内容非常广泛。如何在有限的页面展示空间里呈现更多的内容,是每个门户型网站设计和开发人员需要考虑的。标签的引入使得同一空间可以通过切换展示不同的内容。这一节里我们通过实现体育评论版块的标签来学习这种做法的具体实现。
标签由两个部分组成:标签栏和内容区。其中标签栏同时展示多个标题,用户通过点击或鼠标悬停触发切换动作。为了使用户能够方便地感知到当前所处的位置,选中的标签应该与其它标签有明显的区别。内容区根据当前标签的变化,显示和隐藏对应的内容。从用户感知和美观出发,许多网站在内容区切换时会加入一下切换动画效果。下面我们还是先用HTML结构将网页内容组织好。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>新闻列表-标签</title>
<link rel="stylesheet" href="assets/css/5.css">
</head>
<body>
<div class="fb-comments">
<header class="tabs clearfix">
<div class="tab">红彩专家</div>
<div class="tab current">编辑精选</div>
</header>
<div class="tabs-content">
<div class="content">
<ul>
<li class="cm-item clearfix">
<img class="cm-head" src="assets/image/commentor-xsyl.jpg" alt="肖十一郎">
<div class="cm-info">
<div class="cm-name">肖十一郎</div>
<div>
<span class="cm-status">100%命中</span>
<span class="cm-status">12连红</span>
</div>
<span class="cm-link"></span>
</div>
</li>
<li class="cm-item clearfix">
<img class="cm-head" src="assets/image/commentor-antonio.jpg" alt="Antonio">
<div class="cm-info">
<div class="cm-name">Antonio</div>
<div>
<span class="cm-status">100%命中</span>
<span class="cm-status">10连红</span>
</div>
<span class="cm-link"></span>
</div>
</li>
</ul>
</div>
<div class="content show">
<div class="cm-select">
<div class="clearfix">
<img class="cm-head" src="assets/image/commentor-wq.jpg" alt="王琪">
<div class="cm-info">
<div class="cm-name-sm">王琪</div>
<div>
<span class="cm-pos">足球评论员</span>
<span class="cm-status">14连红</span>
</div>
</div>
</div>
<p>方案<i class="i-line"></i>甄选阿甲赛事 【萨兰迪兵工厂VS普拉腾斯】 </p>
<div class="match">
<i class="i-football"></i><span>阿甲</span>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
<i>
标签本意是italic,将文字设置斜体。在现代前端开发中,推荐使用CSS样式设置字体属性,因此该标签不再推荐使用。但由于i
又是单词icon(图标)的首字母,许多开发者习惯用<i>
标签配合背景图或者图标字体(icon font)来表示网页中修饰性质的图标。
随着样式越来越多,以<style>
标签的形式嵌入到HTML文档中,可能会使得HTML文档很长,不利于维护。将CSS样式写入单独的样式文件,利用<link>
标签引入网页,不但能真正做到结构与样式分离,并且可以提高CSS样式跨网页的复用能力。
<link rel="stylesheet" href="assets/css/5.css">
根据前面的惯例,还是先将容器相关的一些样式设置好:
.fb-comments {
width: 198px;
height: 186px;
font-family: '微软雅黑';
}
在标签布局结构中,要理解联动的概念。虽然在视觉上和行为上,标签和它对应的内容是一个整体,甚至还有共同的边框。但是每一个可视的HTML元素默认所占据的都是一个矩形空间。如果需要形成一个不规则形状,一般需要通过组合多个元素,通过JavaScript形成两个或多个区域的联动。
可能有人会想”左右边框都是一整条包含标签和内容部分在内的线“。其实在前端或者客户端开发的时候,很多时候只是看起来”是“,只需要视觉上看上去相同就行。所以一条线在实现的时候,可能是由多条线拼接而成的。
本模块有两个难点:
-
两种不同的标签状态,选中和默认状态。
-
标签页的显示与隐藏。
2. 标签的实现
两个或多个标签中,为了凸显当前内容所对应的标题,需要让选中状态与默认状态的样式有所不同。可以先设置好默认样式,再用一个单独的状态类设置选中状态独有的样式。
/* 默认状态 */
.tabs {
font-size: 16px;
text-align: center;
}
.tab {
float: left;
width: 50%;
height: 29px;
line-height: 29px;
box-sizing: border-box;
border: 1px solid #eee;
background-color: #f8f8f8;
}
添加一个current
类,用于设置标签选中状态的样式。
.current {
border-top: 2px solid #ff3333;
border-bottom: none;
color: #ff3333;
background-color: #fff;
}
一个元素同时可以设置多个类名,类名之间用空格隔开:
<div class="tab current">编辑精选</div>
使用多个类名,能够更加细粒度地操作元素样式,方便状态切换或者公共样式的重用。比如我们可以定义clearfix
类用于清除浮动。
.clearfix::after {
display: block;
visibility: none;
content: '';
clear: both;
}
在需要清除浮动的元素上,都可以通过添加该类实现清除功能:
<header class="tabs clearfix">
<div class="tab">红彩专家</div>
<div class="tab current">编辑精选</div>
</header>
在实现两个标签水平显示时,还有一个非常重要的属性box-sizing
需要留意一下。如果我们将它从.tab
的样式中移除,显示结果如下:
这是因为默认情况下,CSS盒模型中元素的width
与height
仅表示内容区的大小,而不包括border
和padding
。当子元素的width
设置为50%
并且border
不为0时,其总的宽度要大于父元素宽度的100%
,因此无法在同一行显示。
box-sizing: border-box;
box-sizing
为border-box
时,会将border
和padding
也纳入width
的计算,即width = content + padding + border。
3. 标签页的显示
每个标签都会对应不同的标签页内容,但在同一时刻只会显示一个内容,并且其它不可见内容并不会占据文档流的位置。标签页内容会根据用户的操作进行切换显示。常用有两种方法实现元素的隐藏和显示。
-
display
属性值为none
时,元素会被从文档流中移除。 -
将元素从DOM(文档结构树)中移除或添加元素到DOM。
第一种方法更为常见,并且运行效率更高。元素的显示和隐藏也是状态的改变,因此可以跟标签选中状态切换一样定义一个类进行控制。
.content {
display: none;
border: 1px solid #eee;
border-top: none;
}
.show {
display: block;
}
先定义标签页内容的基础样式为不显示,然后通过添加show
类来控制显示。
<div class="tabs-content">
<div class="content">
</div>
<div class="content show">
</div>
</div>
4. 组合型选择器
随着网页结构越来越复杂,需要添加的样式也随着增多。如果为每一个元素都添加类名,会增加网页大小,同时增加命名难度。CSS选择器可以根据元素的层级关系进行定位:
.content ul {
list-style: none;
margin: 0;
padding: 0 10px;
}
上面的代码表示为具有content
类名的元素内的所有ul
元素设置样式。当两个或多个选择器已空格连接时,表示在前面的选择器所选择的元素中继续查找后一个选择器,并且不考虑层级数。也就是说上面的选择器不但能选中一下结构中的ul
元素:
<div class="content">
<ul>
</ul>
</div>
还能选中下面代码中的ul
元素:
<div class="content">
<div>
<ul>
</ul>
</div>
</div>
如果需要更加严格的限制,只允许选择第一种直接包含关系的情况,应该使用>
符号连接选择器,表示只选择.match
的直接子标签。
.match > span {
color: #888;
}
在本模块中,还有一种用逗号(,
)分隔的组合型选择器需要了解一下:
.cm-head, .cm-info {
float: left;
}
这种选择器表示同时选中.cm-head
和.cm-info
两个选择器所选择的内容。主要应用于需要为多个选择器设置相同样式的场景。
5. 最终样式
.fb-comments {
width: 198px;
height: 186px;
font-family: '微软雅黑';
}
.tabs {
font-size: 16px;
text-align: center;
}
.tab {
float: left;
width: 50%;
height: 29px;
line-height: 29px;
box-sizing: border-box;
border: 1px solid #eee;
background-color: #f8f8f8;
}
.current {
border-top: 2px solid #ff3333;
border-bottom: none;
color: #ff3333;
background-color: #fff;
}
.content {
display: none;
border: 1px solid #eee;
border-top: none;
}
.content ul {
list-style: none;
margin: 0;
padding: 0 10px;
}
.cm-item {
padding: 10px 0;
border-bottom: 1px dashed #eee;
}
.cm-item:last-child {
border-bottom: none;
}
.cm-head, .cm-info {
float: left;
}
.cm-head {
width: 40px;
height: 40px;
border-radius: 20px;
}
.cm-info {
padding-left: 10px;
}
.cm-name {
font-size: 14px;
font-weight: bold;
color: #404040;
}
.cm-status {
height: 16px;
padding: 0 3px;
line-height: 16px;
font-size: 12px;
color: #888;
border: 1px solid #e5e5e5;
border-radius: 5px;
}
.cm-select {
padding: 10px;
}
.cm-name-sm {
font-size: 14px;
color: #404040;
}
.cm-pos {
font-size: 12px;
color: #888;
}
.cm-select p, .match {
color: #404040;
font-size: 12px;
line-height: 15px;
}
.i-line {
display: inline-block;
height: 16px;
border-right: 1px solid #eee;
margin: 0 4px;
vertical-align: middle;
}
.match {
height: 30px;
line-height: 30px;
background-color: #f6f6f6;
}
.i-football {
display: inline-block;
width: 15px;
height: 21px;
margin: 0 4px;
background-image: url(../image/netease_logo.png);
background-repeat: no-repeat;
background-position: 0 -384px;
vertical-align: middle;
}
.match > span {
color: #888;
}
.show {
display: block;
}
.clearfix::after {
display: block;
visibility: none;
content: '';
clear: both;
}