原文:Pro HTML5 Performance
协议:CC BY-NC-SA 4.0
十五、价格控制
价格控制实际上是两个控制,布尔。除了实际的价格控制,我们还有运输控制。Tt:我们把它们放在同一个章节里,因为它们经常放在一起。价格控制包含价格信息,不仅仅是最终价格,还有底价、折扣、税等等。装运控制包含装运信息。
在开始之前,我们想展示一个完成的控件的图像。这是一个单一的图像,因为,再一次,控件经常在一起。
***图 14-1。*价格和运输管制
如果您想查看价格和运输控制的运行情况,请在我们为支持本书而创建的示例网站上查找它们。对于样品价格控制,地址为[
clikz.us/BookExamples/priceControl.php](http://clikz.us/BookExamples/priceControl.php)
价格控制
和我们所有的其他控件一样,这个控件实际上是一个函数和一组应用于函数输出的 CSS 规则。然而,由于没有看到 HTML 很难理解规则,我们从实际上是控件的函数生成的 HTML 开始。
我们看到许多 web 开发人员一直在努力解决的一个问题是,在左对齐的元素(如产品名称)和右对齐的内容(如价格)之间创建一个点前导符。为了帮助人们克服这个问题,价格控件包括一个点前导符,它总是弯曲以填充左对齐元素和右对齐元素之间的空间。
HTML
价格控制的 HTML 并不复杂。它只是一系列的div
元素和span
元素,一个sup
元素保存货币,另一个sup
元素保存价格的小数部分。
注意保存价格信息的 HTML 包含一个运输信息的占位符。
清单 14-1 显示了 HTML 的一部分,它保存了图 14-1 中显示的价格清单的价格信息。
***清单 14-1。*价格控件插入的 HTML 的价格部分
`
Retail Price
$ 1599. 99
.
Discount $ 100. 00 . Your Price $ 1499. 99 . `
CSS
价格控制的 CSS 只包含一些规则。我们分别描述每一个,这样我们就可以讨论每一个是如何工作的,以及这些规则是如何结合在一起的。
在这个过程中,我们还描述了虚线领导是如何工作的。为了获得跨各种背景(包括渐变)工作的虚线前导,我们使用了一个不太为人所知的效果,它是overflow:hidden
属性的一部分。迈克尔从妮可·沙利文的博客中了解到这一点。如果你不熟悉妮可·沙利文的 CSS 工作,花点时间看看她的想法;这是值得花的时间。这里是 W3C CSS 2.1 规范中overflow:hidden
属性的棘手部分:
表格的边框、块级替换元素或建立新的块格式化上下文的正常流程中的元素(如具有“溢出”而非“可见”的元素)不得与元素本身在同一块格式化上下文中的任何浮动重叠。
级联样式表 2 级修订版 1 (CSS 2.1)规范
这可以分解为一个浮点数旁边的overflow:hidden
元素占用了剩余的空间。这就是我们如何让点的可变宽度自动占据标签和数量之间的剩余空间。我们正在创建一个嵌套的overflow:hidden
排列,其中我们使用priceSubWrap
来包含数量,使用div
来包含点分隔符。图 14-2 标注了价格控制的各个部分。
***图 14-2。*价格管制的部分
我们使用另一个 base64 编码的图像作为点。但是,由于 base64 图像不能在 Internet Explorer 6 或 7 中运行,我们还为使用这些浏览器的访问者提供了一个指向该图像的链接。我们对运输栈中的日历图标使用相同的技术。
现在让我们开始看看 CSS 规则。清单 14-2 显示了应用于价格堆栈的第一条规则。它为保存描述和价格的行设置位置和宽度。
***清单 14-2。*设定价格的位置和宽度
.dottedPriceWrap { position: relative; width: 200px; }
清单 14-3 显示了priceLabel
规则,它对价格线的描述部分进行了样式化。特别是,它将float
属性设置为left
(这使得类为priceSubWrap
的div
元素出现在价格标签旁边)并设置填充。
***清单 14-3。*对价格的描述
.priceLabel { float: left; padding-right: 4px; }
清单 14-4 指定了实际的定界符(或前导符)。在这里,我们将overflow
属性的值设置为hidden
。text-indent
属性确保了类别为dottedSpanner
的div
中的文本不会出现。我们必须在那个div
元素中放些东西,否则它会崩溃,而不会显示我们的分隔符,但是我们不希望那个文本出现。正如我们在本章前面所讨论的,base64 编码的数据包含构成我们的点分隔符的点的图像。
提示这种技术提供了一种适用于各种背景(包括渐变)的点状前导。
***清单 14-4。*指定分隔符(圆点)
`.dottedSpanner {
overflow: hidden;
line-height: inherit;
text-indent: -999em;
background: url(data:image/gif;base64,R0lGODlhBwAFAJECAGZmZGJmZf///wAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1w
bWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3
LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5
OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6
Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4
bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JU
b29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUZCQUFFNUJBRjU3
MTFFMUE4NkZBMzc4MEIwMDVGMEQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUZCQUFFNUNBRjU3MTFFMUE4NkZBMzc4
MEIwMDVGMEQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1RkJBQUU1OUFGNTcxMUUx
QTg2RkEzNzgwQjAwNUYwRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1RkJBQUU1QUFGNTcxMUUxQTg2RkEzNzgwQjAw
NUYwRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH/
/v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3
trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBv
bm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgn
JiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAIALAAAAAAHAAUAAAIHjI6Ay+1XAAA7)
repeat-x bottom left;
}
background: url(data:image/gif;base64,R0lGODlhBwAFAJECAGZmZGJmZf///wAAACH/C1hNUCBEYXRhWE1QPD94
cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJh
ZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3
OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5
bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20v
eGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0
cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90
b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NUZCQUFFNUJBRjU3MTFFMUE4NkZBMzc4MEIw
MDVGMEQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NUZCQUFFNUNBRjU3MTFFMUE4NkZBMzc4MEIwMDVGMEQiPiA8eG1w
TU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1RkJBQUU1OUFGNTcxMUUxQTg2RkEzNzgwQjAwNUYw
RCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo1RkJBQUU1QUFGNTcxMUUxQTg2RkEzNzgwQjAwNUYwRCIvPiA8L3JkZjpE
ZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw
7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmo
p6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFg
X15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkY
FxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAIALAAAAAAHAAUAAAIHjI6Ay+1XAAA7) repeat-x bottom left;
}`
清单 14-5 显示了为使用 IE6 或 ie7 的访问者指定在哪里找到点的图像的规则。它还指定图像应该在整行中水平重复。
***清单 14-5。*为 IE6 和 IE7 指定点
.ie6 .dottedSpanner, .ie7 .dottedSpanner { background: url(/img/period.gif) repeat-x bottom left; }
清单 14-6 显示了对价格线的金额部分进行样式化的规则。特别是,它指定价格应该向右浮动,并且有 2 个像素的左填充(以便在分隔符和价格值之间留出一点空间)。
***清单 14-6。*造型价格值
.priceAmt { float: right; padding-left: 2px; }
清单 14-7 中的显示了priceSubWrap
类。这个类应用于保存价格金额和点分隔符的div
。为了让分隔符图像填充价格标签和价格值之间的空间,该类将overflow
属性的值设置为hidden
。它还指定了div
元素的position
属性的值为relative
。
***清单 14-7。*围绕价格和分隔符设计包装器
.priceSubWrap { overflow: hidden; position: relative; }
清单 14-8 设计货币符号($,,€等)的样式。)使用一个简单的规则来指定符号的大小和位置。我们也对小数点右边的价格部分使用这种样式。
***清单 14-8。*设计货币符号
sup.currency { font-size: 70%; top: auto; position: relative; vertical-align: text-top; line-height: 125%; }
控制
price 函数构建一个字符串,其中包含 HTML 元素和参数中指定的值,除了参数$echo
。$echo
参数指示是将 HTML 字符串插入调用该函数的页面,还是将 HTML 作为字符串返回给其他函数。
注意通常,输入这些控件(以及大多数其他控件)的值来自数据库。为了简单起见,我们在示例站点上没有使用数据库。相反,我们只是手工插入值,因为我们的重点是 HTML 和 CSS,而不是后端工作。
表 14-1 描述了price
功能的参数。
清单 14-9 显示了定义价格函数的 PHP 代码。
***清单 14-9。*价格功能
`<?php
function price($label, $currencySymbol, $wholeNumber,
r
e
m
a
i
n
d
e
r
,
remainder,
remainder, echo) {
o
u
t
p
u
t
=
′
<
d
i
v
c
l
a
s
s
=
"
d
o
t
t
e
d
P
r
i
c
e
W
r
a
p
"
>
′
;
output = '<div class="dottedPriceWrap">';
output=′<divclass="dottedPriceWrap">′; output .= ’
$output .= l a b e l ; label; label; output .= ’ ’;
o u t p u t . = ′ < d i v c l a s s = " p r i c e S u b W r a p " > ′ ; output .= ' <div class="priceSubWrap">'; output.=′ <divclass="priceSubWrap">′; output .= ’ ‘;
$output .= ’ ’. c u r r e n c y S y m b o l . ′ < / s u p > < s p a n > ′ . currencySymbol . '</ sup><span>'. currencySymbol.′</sup><span>′.wholeNumber.‘.’. r e m a i n d e r . ′ < / s u p > ′ ; remainder.'</sup>'; remainder.′</sup>′; output .= ’ ‘;
o u t p u t . = ′ < / d i v > ′ ; output .= ' </div>'; output.=′ </div>′; output .= ’ . ’;
$output .= ‘’;
if($echo == “return”){
return $output;
} else {
echo $output;
}
}
?>`
装运控制
正如我们在本章开始时提到的,装运控制是一个独立的控制。但是,因为装运控制通常跟在价格控制之后,所以我们在这里解释装运控制。从 shipping control 函数生成的 HTML 开始,将提供上下文,帮助您理解 CSS 规则和实际控件的功能。
HTML
装运控件的 HTML 包含一个具有三个跨度的单个div
。跨度让我们可以创建单独的规则来设置运输图标、运输标签和运输日期的样式。清单 14-10 显示了保存运输信息的 HTML。
***清单 14-10。*价格控件插入的 HTML 的运输部分
<div class="shippingWrap"> <span class="iconShipping"></span> <span class="shipLabel">Estimated Ship Date: </span> <span class="shipDate">July 25, 2013</span> </div>
到目前为止,你可能已经习惯了我们的 HTML 风格。我们使用具有有意义的类名的简单元素,然后通过 CSS(以及 JavaScript,当我们无法从 CSS 中获得我们想要的东西时)指定如何处理这些元素。因此,让我们研究一下将这些内容转换成价格控件所需输出的 CSS。
CSS
由于装运栈由一个div
和三个跨度组成,我们只需要四个规则来设计装运控制。
清单 14-11 显示了shippingWrap
规则,它指定了装运栈的位置和字体大小。它提供了 22 个像素的左填充值来为日历图标腾出空间,并提供了 5 个像素的上边距值来在价格和运输信息之间留出一点空间。
***清单 14-11。*样式化出货信息
.shippingWrap { position: relative; padding-left: 22px; font-size: 12px; margin-top: 5px; }
清单 14-12 显示了iconShipping
规则,它定义了我们放在运输信息左边的日历图标。我们将一个 18 像素的图像放入一个 22 像素空间的左侧(这 22 个像素是由shippingWrap
规则定义的;参见清单 14-11 ,它在日历图标和发货信息之间提供了一个 4 像素的空间。与分隔符中的点一样,我们使用 base64 编码的数据块来指定图标图像。
***清单 14-12。*插入日历图标
.iconShipping { width: 18px; height: 18px; position: absolute; left: 0; top: 1px; background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAGX RFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABVhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldC BiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bn M6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMC AgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbn MjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLj AvIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYW RvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc2 91cmNlRXZlbnQjIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIy IgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wOkNyZWF0ZURhdGU9IjIwMTItMD YtMThUMjM6MjY6NDktMDU6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDEyLTA2LTIyVDE3OjQyOjUxLTA1OjAwIiB4bXA6TWV0YW RhdGFEYXRlPSIyMDEyLTA2LTIyVDE3OjQyOjUxLTA1OjAwIiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgeG1wTU06SW5zdGFuY2 VJRD0ieG1wLmlpZDpGRTVEQjQyRkI0QjUxMUUxOEJFODlBMDkxM0UyQ0FDNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZD pGRTVEQjQzMEI0QjUxMUUxOEJFODlBMDkxM0UyQ0FDNCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjAxOD AxMTc0MDcyMDY4MTE4QTZEQjZBNUFBMERGQUNCIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0Om FjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDowMTgwMTE3NDA3MjA2ODExOEE2REI2QTVBQTBERk FDQiIgc3RFdnQ6d2hlbj0iMjAxMi0wNi0xOFQyMzoyNjo0OS0wNTowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUG hvdG9zaG9wIENTNSBNYWNpbnRvc2giLz4gPC9yZGY6U2VxPiA8L3htcE1NOkhpc3Rvcnk+IDx4bXBNTTpEZXJpdmVkRnJvbS BzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAxODAxMTc0MDcyMDY4MTE4QTZEQjZBNUFBMERGQUNCIiBzdFJlZjpkb2N1bW VudElEPSJ4bXAuZGlkOjAxODAxMTc0MDcyMDY4MTE4QTZEQjZBNUFBMERGQUNCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3 JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Ih6y4AAAABtQTFRF5eXljcP38fHx1dXVJYnnLZ T0Np7/AAAAGCCkcgAAAAl0Uk5T//8AU094EgAAAD1JREFUeNpi4OBgYkACTBwcDBwMjOxIgBEowMTIhgIYgZrYWF EAG1A3MxoACbGwoCAcQkNZI0oAggMRIMAA9O0D+65FZsYAAAAASUVORK5CYII=); }
清单 14-13 为使用 IE6 或 IE7 的访问者指定了日历图标的位置。
***清单 14-13。*为 IE 6 和 IE7 插入日历图标
.ie6 .iconShipping, .ie7 .iconShipping { background-image: url(/img/icon_calendar.png); }
清单 14-14 显示了shipLabel
规则,它将短语“预计发货日期”设计成灰色阴影,并且是一个块。由于shipLabel
规则位于span
元素上,我们必须将它显示为一个块,以保持它在自己的行上。
***清单 14-14。*对装运块的标签部分进行造型
.shipLabel { color: #777;
display: block; }
现在让我们转到控件的 PHP 部分。
控制
.shipDate
函数做的事情与price
函数非常相似。也就是说,它构建一个包含 HTML 元素的字符串,如果$echo
参数包含"echo"
,则将内容发送给浏览器,或者如果$echo
参数包含"return"
,则将内容发送给调用该函数的函数。但是,它使用不同的参数并创建两行而不是一行。
注意为了描述函数,我们将它们分成两个清单。在我们的示例站点上,clikz.com/BookExamples/priceControl.php
,这两个函数在同一个代码块中。
表 14-2 描述了 shipDate 函数的参数。
正如shipDate
参数的描述所示,该参数只是一个字符串。如果你想确保你总是得到相同的日期格式,把它分成三个参数:年,月,日。如果想要更灵活的控制,甚至可以添加第四个参数来指定日期的格式。因为我们假设我们所有的(虚构的)客户都在美国,所以我们将使用典型的美国日期格式。
清单 14-15 显示了定义shipDate
函数的 PHP 代码。
***清单 14-15。*发货日期功能
<?php function shipDate($shipDateLabel, $shipDate, $echo){ $output = '<div class="shippingWrap">'; $output .= ' <span class="iconShipping"></span>'; $output .= ' <span class="shipLabel">'; $output .= $shipDateLabel; $output .= '</span>'; $output .= ' <span class="shipDate">'; $output .= $shipDate; $output .= '</span>';
` $output .= ‘’;
if($echo == “return”){
return $output;
}else {
echo $output;
}
}
?>`
使用控件
要使用这些控件,您必须将php
标签插入您的网页。每个价格和运输控制都需要一个单独的php
标签。考虑一下我们在本章中使用的例子。图 14-3 显示了我们的示例价格堆栈(由三个价格控件和一个运输控件组成)。它与图 14-1 的图像完全相同,为了方便起见,在此重复一遍。
***图 14-3。*一个简单的价格栈(再次)
为了创建我们的示例价格栈,我们需要四个php
标记,每个标记调用价格控件或运输控件。清单 14-16 显示了创建价格栈的函数调用,如图图 14-3 所示。
***清单 14-16。*使用价格和运输控制
`<?php price("Retail Price", "$", "1599", "99"); ?>
<?php price("Discount", "$", "100", "00"); ?> <?php price("Your Price", "$", "1499", "99"); ?> <?php shipDate( "Estimated Ship Date:", "July 25, 2013") ?>`总结
价格控件实际上是两个控件,一个用于在价格堆栈中生成单独的行,另一个用于创建运输信息。这种关注点的分离提供了许多好处。首先,您可以堆叠任意数量的价格堆叠行(也许是为了显示各种折扣或费用)。另一方面,您可以将运输信息放在价格栈的顶部或底部(甚至在价格线之间,如果您能想到一个好的理由这样做)。您也可以省去一个控件或另一个控件,让定价单独存在,或者让装运块单独存在。
我们将在下一章中同时使用价格控制和运输控制,其中我们描述了产品控制(或者我们通常称之为产品堆栈)。正如我们不断重复的那样,我们的大多数控件都由其他控件组成,尽管通常带有附加信息。记住分形的比喻:如果你放大一个分形图像,你会看到一个非常相似的图像。类似地,如果你查看我们的一个控件,你可能会发现更多的控件。
十五、产品控制
在前面的章节中,我们已经提到了产品控件,它的大部分内容都使用其他控件。因为它是其他控件的堆叠,并且它通常的布局是垂直的,一项信息堆叠在另一项上,所以我们通常称它为产品堆叠。该控件还可以具有更水平的布局,其中内容出现在两个不同大小的列中。正如我们对其他对照品所做的那样,我们将产品的变异称为对照品处理。它有两种处理方式:垂直和水平。
在我们开始这个控件之前,让我们花一分钟回忆一下我们制作所有这些控件的原因:提高性能。最大的性能收益无疑属于开发人员。他们不用创建自己的产品列表,他们可以使用我们的控件,只需花很少的时间就可以获得产品列表。此外,因为控件将它们的代码封装在一个容易找到的地方,所以调试更加容易和快速,这是开发人员的另一个性能改进。游客也可以看到性能的提升。当每种产品看起来都有点不同时,区分这些差异就成了一个挑战。但是,由于从一个产品列表到下一个产品列表有一致性的保证,区别很容易发现,访问者可以很快找到他们想要的产品。最后,如果访问者更容易找到他们想要的,他们不仅会买更多,而且会告诉他们的朋友他们找到了一个多么好的网站。这对企业来说是一大胜利。
产品控件使用链接控件(提供帮助链接)、价格控件和按钮控件。它还添加了产品的图像和一些可能会吸引客户购买产品的文本。(在我们的例子中,我们使用了无意义的文本,但我们并不是真的想出售任何东西。)
如果您想在网页上看到产品堆栈,您可以在我们的示例站点找到:[
clikz.us/BookExamples/productControl.php](http://clikz.us/BookExamples/productControl.php)
图 15-1 显示了使用中的产品控制。在垂直方向上,它占据了整个页面(这是在我们将它缩小一点之后,这样我们就可以在带有图像的页面上看到标题)。因为产品控件提供了大量的信息,所以占用了大量的空间。
***图 15-1。*产品控制
产品控件的水平布局占用相同的空间,但是对于书籍布局来说更友好一些。我们发现垂直布局在网页上效果最好,我们可以将许多产品控件放在一起,帮助客户比较产品。也就是说,水平布局也有其 web 用途,尤其是当它是页面上唯一的产品控件时。
图 15-2 显示了产品控制的水平布局。
***图 15-2。*产品控制的横向布局
产品控件中的大部分内容,无论是垂直的还是水平的,看起来都很熟悉。帮助链接(圆圈中的问号)与第十一章中的帮助链接相同。装运区(日历图标、“预计装运日期”文本和实际日期)与在第十三章中看到的装运区相同。这三行价格信息也与在第十三章中看到的相同。“给我买”按钮是第十二章中描述的按钮控件的一个实例。
现在您已经看到了产品控件的样子,是时候学习如何让它工作了。让我们从如何在网页中放置一个开始。
插入控件
为了指定产品控件的水平或垂直处理,我们从页面中调用一个 PHP 函数,并指定两个参数:一个数据源和一个处理。我们稍后将讨论数据源。现在,就把它想象成我们在运行时在控件中处理的一个数据块。
垂直处理的名称为pcTreatment1
(用于产品控制处理 1)。这不是最原始或最具描述性的名字,但它遵循了我们通常的命名惯例。横向处理的名字(你大概已经猜到了)是pcTreatment2
。
对于我们的示例页面,我们首先创建垂直布局,然后创建水平布局,两者之间有几个空行以提供一些空白空间。清单 15-1 显示了在网页中创建产品控件的两种不同处理的代码。
***清单 15-1。*将产品控件放到页面上
`
pcTreatment1
<?php productStack(json_decode($productJSON), "pcTreatment1"); ?>pcTreatment2
<?php productStack(json_decode($productJSON), "pcTreatment2"); ?>`现在你已经知道了输入是什么样子,让我们考虑一下输出。
由控件产生的 HTML
控件创建在页面中使用的 HTML。我们提供 HTML 以便您可以看到控件在运行时产生的内容。HTML 嵌入在控件中,我们将在后面展示,但是它本身和所有的值都在适当的位置更容易阅读。
当你通读清单 15-2 时,注意类名。制作这种控件的诀窍在于类名。HTML 元素实际上只包含数据。一个有经验的 CSS 用户知道,给定不同的 CSS 规则,你可以从相同的元素制作完全不同的布局。这就是我们如何为我们的控制实现不同的处理。我们依靠类名来给我们控制任何给定处理的布局的入口点。因此,整个展示依赖于类名。
注意 清单 15-2 显示了只是垂直处理的 HTML 结果。清单已经很大了,所以我们没有通过添加横向处理使它更大。此外,HTML 中处理的唯一区别是pcTreatment1
和pcTreatment2
。这两种处理之间的所有其他差异都在 CSS 规则中。
清单 15-2 显示了垂直处理的 HTML,所有的值都在适当的位置。我们添加了一些注释来指出产品控件中的其他控件。为了便于查找,我们用粗体显示这些注释。
***清单 15-2。*垂直处理的产品控件创建的 HTML】
<article class="productControlWrap pcTreatment1"> <img src="/img/laptop1.jpg" alt="This is a picture" class="pcImage"><h2 class="pcTitle">The Worlds Best Laptop</h2> ***<!-- Here's the top of a price stack -->*** <div class="pcPriceStack"> <div class="dottedPriceWrap"> <div class="priceLabel"> Retail Price </div> <div class="priceSubWrap"> <div class="priceAmt"> <sup class="currency">$</sup><span class="wholeNumber">1599.</span><sup class="currency">99</sup> </div> <div class="dottedSpanner"> . </div> </div> </div> <div class="dottedPriceWrap"> <div class="priceLabel"> Discount
</div> <div class="priceSubWrap"> <div class="priceAmt"> <sup class="currency">$</sup><span class="wholeNumber">100.</span><sup class="currency">00</sup> </div> <div class="dottedSpanner"> . </div> </div> </div> <div class="dottedPriceWrap"> <div class="priceLabel"> Your Price </div> <div class="priceSubWrap"> <div class="priceAmt"> <sup class="currency">$</sup><span class="wholeNumber">1499.</span><sup class="currency">99</sup> </div> <div class="dottedSpanner"> . </div> </div> </div> </div> ***<!-- End of price stack control -->*** *** <!-- Here's the shipping stack,*** *** which is not within the price stack -->*** *** <!-- Remember, the price control can create produce price lines*** *** and shipping blocks independently of one another. -->*** <div class="pcShippingStack"> <div class="shippingWrap"> *** <!-- Note the anchor element; that is the help link control -->*** <span class="iconShipping"></span><span class="shipLabel"><a href="javascript:;" data- link-type="help" class="link">Estimated Ship Date<span class="linkIcon" data-tooltip- message="You will receive a definitive date during checkout"></span></a> </span><span class="shipDate">July 25, 2013 </span> </div> </div> ***<!-- End of shipping stack -->*** <div class="pcDescWrap"> <div class="pcDescText"> Sartorial williamsburg small batch helvetica mixtape wayfarers. Art party biodiesel before they sold out authentic. </div> <ul class="pcDescBullets"> <li class="pcDescLI"> Squid pinterest carles, fingerstache forage scenester. </li> <li class="pcDescLI"> Pinterest carles, fingerstache forage scenester. </li>
`
Carles, fingerstache forage scenester.
< !-- Here’s a button control, constituted as a link, since it’s not in a form -->
Buy Me `如果我们不在控件中使用控件,这个列表会更大。如果没有嵌套控件,我们就必须包含每个包含控件的所有元素。压缩代码的能力很有帮助,既能保持每个页面的代码更小,又能帮助我们阅读现有的代码。我们可以在清单 15-2 中查看锚元素的属性,立即知道一个是弹出帮助链接,另一个是购买按钮。代码
type="help"
给出了一个相当大的线索,后面跟着class="button primary shadow"
和Buy Me
。与任何语言中的大多数代码一样,学习发现代码中的东西只是一个练习的问题。如果你像我们一样整天和控制打交道,你也会在一瞬间发现它们。CSS
与所有其他控件一样,CSS 以一种吸引人的方式安排产品控件(我们希望如此)。在本例中,我们有两个处理,我们需要为它们安排相同的内容(不包括类名)。由于一些规则适用于两种处理方式,所以我们在 CSS 中有三个逻辑部分:适用于两种处理方式的规则,仅适用于垂直处理方式的规则(
pcTreatment1
),以及仅适用于水平处理方式的规则(pcTreatment2
)。我们将为每个规则组提供自己的列表。清单 15-3 显示了适用于产品控件的垂直和水平处理的 CSS 规则。
***清单 15-3。*产品控制两种处理的 CSS 规则
*/** * * Starting with the base definition that are shared* * * across all of the productControls. Besides the treatment* * * for the image, we're basically reseting margins and setting* * * initial font-sizes for consistency. We want to keep the styling* * * light so we reduce the number of times we have to override* * * the styles in the treatment specific sections.* * */* .productControlWrap { position: relative; font-size: 14px; } .pcImage { -webkit-box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); -moz-box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); border: 1px solid #999; border: 1px solid rgba(0, 0, 0, 0.3); } .pcTitle {
font-size: 18px; margin: 0; line-height: 110%; color: #1f81dd; } .priceLabel { color: #555; } .priceAmt { color: green; font-weight: bold; } .pcDescBullets { margin: 0; padding-left: 20px; }
清单 15-4 展示了只适用于产品控件的垂直处理的 CSS 规则。如果您记得垂直处理的名称是
pcTreatment1
,这个清单会更有意义。清单 15-4。 CSS 规则仅针对产品控件的纵向处理
*/** * * Styles to affect the pcTreatment1 look.* * ** * */* .pcTreatment1 { width: 200px; color: #333333; } .pcTreatment1 .pcImage { width: 200px; height: 200px; margin-bottom: 10px; } .pcTreatment1 .pcTitle { margin-bottom: 10px; } .pcTreatment1 .shippingWrap { margin-bottom: 10px; } .pcTreatment1 .pcDescText { margin-bottom: 10px; line-height: 150%; } .pcTreatment1 .pcDescLI { margin-bottom: 10px; }
清单 15-5 显示了仅适用于产品控件水平处理的 CSS 规则。如果您记得水平处理的名称是
pcTreatment2
,那么这个列表就更有意义了。清单 15-5。 CSS 规则仅针对产品控制的横向处理
*/** * * Styles to affect the pcTreatment2 look.* * ** * */* .pcTreatment2 { width: 300px; padding-left: 230px; color: #333333; } .pcTreatment2 .pcImage { width: 200px; height: 200px; position: absolute; left: 0; top: 0; -webkit-box-shadow: 0 4px 4px 0px rgba(0, 0, 0, 0.4); -moz-box-shadow: 0 4px 4px 0px rgba(0, 0, 0, 0.4); box-shadow: 0 4px 4px 0px rgba(0, 0, 0, 0.4); } .pcTreatment2 .pcTitle { font-size: 24px; margin-bottom: 20px; } .pcTreatment2 .pcPriceStack { width: 200px; margin-bottom: 10px; } .pcTreatment2 .shippingWrap { position: absolute; left: 0; top: 220px; } .pcTreatment2 .shipLabel .link { color: #333333; } .pcTreatment2 .shipLabel .link:visited { color: #333333; } .pcTreatment2 .pcDescText { margin-bottom: 10px; line-height: 150%; } .pcTreatment2 .pcDescLI { margin-bottom: 10px; } .pcTreatment2 .button { position: absolute; left: 0; top: 270px;
}
因为这些规则并不复杂,所以没有必要像我们在其他章节中所做的那样分别描述每一个。这实际上是从其他控件构建控件的另一个好处:复杂性可以隐藏。我们将 CSS 规则用于链接、价格和按钮控件,但我们不必关心这些规则,因为它们是这些控件的一部分,我们在这里只是使用(而不是定义)它们。很高兴知道一旦一个控件工作了,我们就可以用同样的方式来使用它,就像某人将控件链接在一起制作一个页面一样。这就是封装的力量。多棒的东西!
产品控制的数据
产品控件需要足够的信息,试图通过函数参数提供字符串将会非常困难,并且很可能导致错误。相反,我们传入一个数据对象。在我们的例子中,我们选择使用一个 JSON (JavaScript Object Notation)对象,因为 JSON 是一个已经建立的标准,我们非常熟悉并且使用很多。
如果您不熟悉 JSON,花几分钟学习一下基础知识。JSON 在网络上的家是
[
www.json.org](http://www.json.org)
(然而,对于快速浏览来说,它有点密集)。你可能会发现 W3 Schools ([
www.w3schools.com/json/default.php](http://www.w3schools.com/json/default.php)
)的 JSON 教程更容易快速阅读。检查我们的数据源,您会看到我们创建了一堆名称-值对。每一对都用逗号隔开。在每个名称-值对中,名称与值之间用冒号隔开。每个值,无论是名称还是值,都是一个字符串,因此用引号括起来。
仔细观察,您还应该看到一些不同的分组机制。例如,
priceStack
名称的值是一组数据。在该集合中,还有一些用大括号括起来的集合。这是一种编码方式,即价格堆栈控件的数据由值的集合组成,每个值由定义价格堆栈控件中的价格线的数据组成。一旦你学会了阅读 JSON 的语法,它就非常方便。当然,处理所有数据的诀窍是解析它们,以便它们最终出现在正确的位置。我们将在下一节中讨论这个问题,我们将描述控件的 PHP 代码。
清单 15-6 显示了包含产品控制数据的 JSON 对象。
***清单 15-6。*包含产品控制数据的 JSON 对象
<?php $productJSON = '{ "title":"The Worlds Best Laptop", "img":"/img/laptop1.jpg", "imgAlt" : "This is a picture", "priceStack" : [ {"label":"Retail Price", "currencySymbol" : "$", "wholeNumber": "1599", "remainder":"99"}, {"label":"Discount", "currencySymbol" : "$", "wholeNumber": "100", "remainder":"00"}, {"label":"Your Price", "currencySymbol" : "$", "wholeNumber": "1499", "remainder":"99"} ], "shipDate": {"shipLabel":"' . mLink("Estimated Ship Date", "javascript:;", "help", NULL, "return", NULL, "You will receive a definitive date during checkout") . '", "shipDate":"July 25, 2013"}, "descText": "Sartorial williamsburg small batch helvetica mixtape wayfarers. Art party biodiesel before they sold out authentic.", "descBullets": [ {"bulletText": "Squid pinterest carles, fingerstache forage scenester."},
{"bulletText": "Pinterest carles, fingerstache forage scenester."}, {"bulletText": "Carles, fingerstache forage scenester."} ], "button" : {"text": "Buy Me", "href": "/addToCart.php", "type": "primaryShadow"} }'; ?>
现在你已经看到了控件的输出、CSS 和数据,是时候看看完成所有工作的 PHP 代码了。
控制
虽然这是一个相当大的列表,但构成产品堆栈的函数背后的概念实际上相当简单:我们将属于控件的数据与最终包含该数据的 HTML 元素混合在一起。为此,我们构建一个大字符串,它包含 HTML 并在适当的位置包含所有适当的数据值。这就是我们所有的控件所做的一切,但它值得重复,因为人们有时会过度思考控件应该做什么,最终给自己带来很多麻烦。
正如我们已经提到的,我们在这个例子和我们的示例网站上使用 PHP。然而,任何可用于 web 开发的语言都同样适用(甚至更好,如果你碰巧对另一种语言比对 PHP 更熟悉的话)。我们认为这个想法也值得重复,因为我们希望您认识到算法(构建一个包含满载数据的 HTML 的字符串)是重要的部分。用于编码控件的语言并不重要——当然,只要它能完成工作。
构建产品控制的功能称为
productStack
。它有两个参数:一个名为$obj
的对象,包含数据;一个名为$treatment
的字符串,指示创建垂直布局还是水平布局。$treatment
参数的唯一有效值是pcTreatment1
(对于垂直布局)和pcTreatment2
(对于水平布局)。任何其他值都将导致客户浏览器中出现令人讨厌的输出。如果我们更谨慎一点,我们会验证$treatment
参数的值,并以某种优雅的方式失败(比如不显示任何内容,而是将一条错误消息写入日志文件)。如果你通读这个函数,你会看到我们首先创建了一个名为
$output
的变量。我们通过将最外层元素类(一篇文章)的开始标签放入$output
变量中来启动这个函数。该函数的其余部分大部分由一系列检查数据对象内容的“if”语句组成。如果我们没有获得数据对象中某个特定名称的值,我们就不会将那部分 HTML 添加到输出字符串中。最后,我们添加最外层元素的结束标记,并将输出回显到浏览器,这导致调用函数的行被我们在那个长字符串中构建的 HTML 所替换。很简单,真的。但是,因为产品控件有很多可能性——一个图像、多个价格值、运输信息、多个描述性元素等等——所以列表看起来可能很复杂。请记住,我们正在构建一个保存 HTML 的字符串,方法是检查我们的数据对象中是否存在每种数据,如果发现了这种数据,就追加更多的元素。同样,这是构建控件的基本算法。
注意,我们注释掉了检查按钮所需数据的
if
语句。什么样的商品列表没有购买按钮?(其实我们可以想出几个这样的用例,但不会下那个兔子洞。这是我们在这个例子中不需要的复杂性。)清单 15-7 显示了我们用来创建产品控件的 HTML 输出的 PHP 代码。
***清单 15-7。*产品控件背后的 PHP 代码
<?php function productStack($obj, $treatment) {
`// Start with our containing element and add a product control treatment if applicable
o u t p u t = ′ < a r t i c l e c l a s s = " p r o d u c t C o n t r o l W r a p ′ . output = ' <article class="productControlWrap '. output=′ <articleclass="productControlWrap′.treatment.‘">’;
// If there’s an image defined output the image tag
if(KaTeX parse error: Expected '}', got 'EOF' at end of input: …= NULL){ output .= ’ ‘;
}
// If there’s a title defined output it
if(KaTeX parse error: Expected '}', got 'EOF' at end of input: …= NULL){ output .= ’’.KaTeX parse error: Expected 'EOF', got '}' at position 25: …e.'</h2>'; }̲ // If ther…obj->priceStack != NULL){
o u t p u t . = ′ < d i v c l a s s = " p c P r i c e S t a c k " > ′ ; f o r ( output .= '<div class="pcPriceStack">'; for ( output.=′<divclass="pcPriceStack">′; for(i=0; i < s i z e o f ( i < sizeof( i<sizeof(obj->{‘priceStack’}); KaTeX parse error: Expected '}', got 'EOF' at end of input: … work. output .= price(
o b j − > p r i c e S t a c k [ obj->priceStack[ obj−>priceStack[i]->{‘label’},
o b j − > p r i c e S t a c k [ obj->priceStack[ obj−>priceStack[i]->{‘currencySymbol’},
o b j − > p r i c e S t a c k [ obj->priceStack[ obj−>priceStack[i]->{‘wholeNumber’},
o b j − > p r i c e S t a c k [ obj->priceStack[ obj−>priceStack[i]->{‘remainder’},
“return”
);
}
KaTeX parse error: Expected 'EOF', got '}' at position 25: … '</div>'; }̲ // If ther…obj->shipDate != NULL){
// the shipDate function is an instance of our shipping control. See chapter 14.
o u t p u t . = ′ < d i v c l a s s = " p c S h i p p i n g S t a c k " > ′ . s h i p D a t e ( output .= ' <div class="pcShippingStack">'. shipDate( output.=′ <divclass="pcShippingStack">′.shipDate(obj-shipDate->shipLabel, KaTeX parse error: Expected 'EOF', got '}' at position 52: … '</div>'; }̲ // If ther…obj->descText != NULL || KaTeX parse error: Expected '}', got 'EOF' at end of input: …= NULL){ output .= ’
‘;
}
// If there’s descriptive text defined output it
if( KaTeX parse error: Expected '}', got 'EOF' at end of input: …= NULL){ output .= ’ ’;
$output .= o b j − > d e s c T e x t ; obj->descText; obj−>descText; output .= ’ ‘;
}
// If descriptive bullets are defined, output them
if( KaTeX parse error: Expected '}', got 'EOF' at end of input: …= NULL){ output .= ‘-
’;
for ($i=0; i < s i z e o f ( i < sizeof( i<sizeof(obj->{‘descBullets’}); KaTeX parse error: Expected '}', got 'EOF' at end of input: …+) { output .= ’ - ’. o b j − > d e s c B u l l e t s [ obj->descBullets[ obj−>descBullets[i]->{‘bulletText’}.‘
- ’;
}
KaTeX parse error: Expected 'EOF', got '}' at position 24: …= '</ul>'; }̲ // Closing…obj->descText != NULL || KaTeX parse error: Expected '}', got 'EOF' at end of input: …NULL){` ` output .= ’
}
// If there’s a button defined output it
//if( KaTeX parse error: Expected '}', got 'EOF' at end of input: … controls. output .= button(
$obj->button->{‘text’},
$obj->button->{‘href’},
NULL,
NULL,
KaTeX parse error: Expected 'EOF', got '}' at position 156: … ); //}̲ output .= ’ ’;echo $output;
}
?>`总结
本章讲述了电子商务网站的另一个基本控制。一个电子商务网站必须有产品列表和购买这些产品的方法。产品控制满足这两个业务目标。
在本章中,我们展示了几项关键技术:
- 在控件中使用控件;
- 使用数据对象为控件提供值;
- 通过类名将 CSS 附加到控件的输出 HTML
- 实现使控件工作的基本算法。
由于该算法是我们的控件如何工作的核心,我们想在本章结束时再次提醒您。使控件工作的本质是构建一个 HTML 字符串,该字符串包含网页上所需的元素和数据。当访问者在 web 浏览器中查看网页时,该字符串将替换您在网页中放置的功能。虽然这不是迈克尔所说的火箭手术(他最喜欢的短语之一),但这是一种非常强大的创建网页的方法。
十六、表格控件
作为一名前端开发人员,Michael 对表格又爱又恨。一方面,表格提供了一种近乎完美的方式来显示表格数据。另一方面,一些开发人员滥用表格作为几乎任何事情的布局机制。Michael 经常看到开发人员(通常是被迫做前端工作的后端或中间件开发人员)使用嵌套表格来移动内容几个像素。对于知道它应该是什么的人来说,看到这种事情是非常令人沮丧的。我们相信许多读者都有同感。
我们假设你以前听过“表格是布局的恶魔”这种说法。为了火上浇油,我们有了响应式设计。除了不使用表格进行布局的所有其他原因(包括较低的性能、要呈现的 HTML 元素的增加以及难以阅读的代码),还有一个额外的问题是表格不能响应响应式设计。桌子是内部固定的;他们不允许对 TD 元素重新排序(就像 CSS 一样)。例如,如果一个有两栏的表格(左边是导航栏,右边是内容栏)用于基本的页面布局,那么这个页面就不容易重新排列。如果不从服务器创建一个新页面,导航就不能移动到顶部或右侧(这会对性能产生巨大影响)。
那么表和性能呢?在前两段中已经提到了几个与性能相关的问题,现在让我们更深入地探讨一下这个问题。就客户端(即浏览器)性能而言,表的一个大问题是,当浏览器呈现它们时,它们通常会强制执行一个或多个重绘操作。即使您遵循了在页面顶部加载样式、在页面底部加载脚本的原则,即使您的表格具有相当规则的内容,它们也经常会触发重绘事件。这些重绘事件大大降低了渲染速度(从而降低了页面加载时间),因为渲染引擎每次都必须从表的顶部开始。在服务器端,如果你为每个布局使用不同的页面(例如,垂直和水平),一些糟糕的访问者将手机旋转 90 度会导致一个全新的页面加载。除了让访问者抓狂之外,您还添加了一整套 HTTP 请求并增加了带宽使用,这都是因为有人希望看到您的信息得到更广泛的显示。最后,如果您创建一个接受数据流并生成一个表的控件,开发人员的性能会得到提高。即使手工编写一个小表也需要时间,至少与将数据从数据库传递给函数相比是这样。
由于性能问题(特别是在这种情况下的开发人员性能)和所有其他问题,我们重申许多其他人的建议:不要使用表格进行布局。但是如何在移动设备中正确使用表格——表格数据?这方面的问题是水平空间有限;大多数表格都需要向右滚动——这在网络上是不允许的,除非你用的是 Windows phone。因为我们遵循移动优先的模式,所以我们设计所有的页面,让它们在手机上看起来尽可能的好。因此,我们将分享 Michael 想出的解决这个问题的技术:用 CSS 样式的嵌套列表来呈现表格。听起来熟悉吗?这就是我们在第七章中构建导航的方式,当然,在这里我们使用不同的风格。
在本章中,我们将展示如何构建一个示例表格控件。您可以在我们示例站点上找到该控件:
[
clikz.us/BookExamples/tableControl.php](http://clikz.us/BookExamples/tableControl.php)
在开始描述控件如何工作之前,我们认为应该向您展示它在两种状态下的样子:宽和窄。(同样,我们称每种状态为一种治疗,就像我们控制的所有变化一样。)我们还将在表格中显示一个突出显示的单元格。
图 16-1 显示了我们的表格控件的宽处理。虽然这看起来像一个标准表,但它实际上是一个包含一堆列表(
ul
元素及其li
子元素)的div
元素。它的行为和外观都像一个标准表。在控件内部,我们展示了一种将表格转换为标准表格元素的方法。我们提供了使用表格元素的能力,因为 IE8 之前的 Internet Explorer 不支持显示表格数据的嵌套列表方法。我们使用浏览器检测来显示 IE6 和 IE7 的正确内容。***图 16-1。*我们样品台的宽布局
图 16-2 显示了我们的悬停功能。当访问者的鼠标悬停在一个单元格上时,整行高亮显示,鼠标悬停的单元格进一步高亮显示(变暗)。在突出显示行和单元格时,与访问者正在考虑的选择相关的细节也被突出显示。正如我们在本章后面所展示的,所有这些突出显示都是通过 CSS 来控制的。
***图 16-2。*带有高亮(深色)单元格的宽布局
图 16-3 显示了我们的表格控件的窄(因此更高)处理。这种处理可能对游客在手机或其他有限显示设备上查看桌子有用。当页面设计者希望将表格信息放在页面的一侧,而将其他信息放在页面的另一侧时,窄视图也很有用。例如,一个音乐网站可能有一个歌曲和演唱这些歌曲的艺术家的列表,并附有这些歌曲的共同点的描述。
注意正如我们之前提到的,随着设备的快速增长,我们不能只计划手机和桌面浏览器。据我们所知,有人可能会在冰箱门上或柜门内侧或我们想象不到的地方看到我们的餐桌控制。因此,我们根据布局特征(窄与宽)而不是目标设备(桌面与电话)来思考(并命名我们的控制和处理)。
***图 16-3。*我们表控的狭隘待遇
除了改变类名以反映预期的处理之外,窄处理使用与宽处理完全相同的 HTML。我们刚刚使用 CSS 为窄显示屏设备旋转了表格。说到 HTML,让我们看看我们的例子背后的 HTML。
HTML
我们示例表格的 HTML 是由表格控件生成的。如前所述,它由一个
div
元素组成,其内容是一组无序列表(宽布局中每行一个列表,窄布局中每个块一个列表)。当您阅读 HTML 时,请注意
div
元素上的类名。它们表明这个div
包含一个表的内容以及使用哪种处理方式(tableTreatment1
是宽处理方式,tableTreatment2
是窄处理方式)。类似地,ul
元素中的样式名表明一行是标题行还是正文行。另外,li
元素中的样式名称表明该元素是一个单元格,以及任何给定的单元格是否是第一个单元格。在li
元素中,data-colhead
属性(一个自定义属性)标识单元格所属的列。我们使用 data-colhead 属性的值来提供窄处理中的标题信息,这支持表的旋转。注意 如果包含我们的样本站点上使用的所有数据,清单 16-1 会很长。制作一个像样的样本需要相当多的数据,但是这导致了清单中的大量重复。因此,我们只包括定义标题行的列表和定义第一行内容的列表。第一个列表定义了标题行。第二个列表定义了第一个正文行。因为正文行在结构上是相同的,只是内容不同,所以您可以通过检查其中一行来了解它们是如何工作的。要查看整个列表并查看实际表格,请转到
[
clikz.us/BookExamples/tableControl.php](http://clikz.us/BookExamples/tableControl.php)
清单 16-1 展示了本章前面展示的样本背后的 HTML。
***清单 16-1。*我们的样本表的 HTML】
<div class="table tableTreatment1"> <ul class="tr tableHead"> <li class="td " data-colhead=""></li> <li class="td " data-colhead="The Head"> The Head </li> <li class="td " data-colhead="Top of the Stack"> Top of the Stack </li> <li class="td " data-colhead="Don't Forget Me"> Don't Forget Me </li> <li class="td " data-colhead="Austin blog"> Austin blog </li> <li class="td " data-colhead="The Best for Last"> The Best for Last </li>
`
Irony mumblecore
Letterpress authentic
Salvia hella raw
Austin blog bicycle
Chambray 8-bit post-ironic
Master cleanse hoodie
数据对象
既然您已经看到了示例表后面的 HTML,那么让我们继续看 HTML 中的数据。表格控件将数据转换成 HTML,类似于我们在清单 16-1 中看到的 HTML。table 控件需要一个数组的数组,其中第一个内部数组保存标题行的数据(注意第一个值是空字符串),其余的内部数组保存正文行的数据。当然,在更现实的场景中,您可以通过查询数据库并为您构建列表的函数或对象从数据库中获取这些数据。因为我们的示例站点没有使用数据库(重点只放在本书的前端),所以我们使用静态数据。
清单 16-2 显示了我们的样本表背后的数据。
***清单 16-2。*进入我们样本表的数据
<?php $tableObj = '[ ["","The Head","Top of the Stack","Don\'t Forget Me","Austin blog", "The Best for Last"], ["Irony mumblecore","Letterpress authentic","Salvia hella raw","Austin blog bicycle","Chambray 8-bit post-ironic","Master cleanse hoodie"], ["Cosby sweater cred","Chambray 8-bit post-ironic","Letterpress authentic","Austin blog bicycle","Irony mumblecore","Master cleanse hoodie"], ["Chambray 8-bit post-ironic","Letterpress authentic","Austin blog bicycle","Synth jean shorts","Cosby sweater cred","Cosby sweater cred"], ["Salvia hella raw","Cosby sweater cred","Master cleanse hoodie","Irony mumblecore","Austin blog bicycle","Letterpress authentic"], ["Master cleanse hoodie","Chambray 8-bit post-ironic","Irony mumblecore","Austin blog bicycle","Letterpress authentic","Salvia hella raw"],
["Austin blog bicycle","Letterpress authentic","Salvia hella raw","Cosby sweater cred","Irony mumblecore","Chambray 8-bit post-ironic"] ]'; ?>
表格控件
如前所述,我们在示例站点中使用了 PHP。然而,任何支持 web 开发的语言都同样适用。在我们自己的工作中,多年来我们一直使用 Java 和 C#以及其他语言。
在控件的顶部,我们检查参数
legacyBrowser
的值,并将我们编写的元素设置为div
、ul
和li
(对于大多数浏览器)或table
、tr
和td (for versions of Internet Explorer prior to IE8)
。控件的大部分其余部分由一对嵌套的
for
循环组成,这些循环遍历数据并写入适当的元素及其属性和内容。控件的最后一部分将 HTML 回显到浏览器。清单 16-3 显示了构成表格控件的 PHP。
***清单 16-3。*定义表格控件的 PHP 代码
`<?php
function tableControl($model, $treatment, KaTeX parse error: Expected group as argument to '\.' at position 222: …d // in IE7\. ̲We can use brow…tableElement = ( l e g a c y B r o w s e r = = T R U E ? " t a b l e " : " d i v " ) ; legacyBrowser == TRUE ? "table" : "div"); legacyBrowser==TRUE?"table":"div"); trElement = ( l e g a c y B r o w s e r = = T R U E ? " t r " : " u l " ) ; legacyBrowser == TRUE ? "tr" : "ul"); legacyBrowser==TRUE?"tr":"ul"); tdElement = ($legacyBrowser == TRUE ? “td” : “li”);$output = ‘<’ . $tableElement . ’ class=“table ’ . t r e a t m e n t . ′ " > ′ ; f o r ( treatment . '">'; for ( treatment.′">′; for(i = 0; i < s i z e o f ( i < sizeof( i<sizeof(model); KaTeX parse error: Expected '}', got 'EOF' at end of input: i++) { if (i == 0) {
KaTeX parse error: Expected 'EOF', got '}' at position 29: …ableHead"; }̲ else { rowClass = “tableBody”;
}
$output .= ‘<’ . $trElement . ’ class=“tr ’ . r o w C l a s s . ′ " > ′ ; f o r ( rowClass . '">'; for ( rowClass.′">′; for(j = 0; j < s i z e o f ( j < sizeof( j<sizeof(model[$i]); KaTeX parse error: Expected '}', got 'EOF' at end of input: …der row. first = (($j == 0 && i > 0 ) ? " f i r s t " : " " ) ; / / W e ′ r e i t e r a t i n g t h r o u g h t h e ′ t d ′ s h e r e b u t a l s o a d d i n g i n t h e c o l u m n h e a d t e x t a s a / / d a t a a t t r i b u t e i n c a s e w e / / w a n t i t l a t e r . Y o u c a n p u t a c o n d i t i o n a l a r o u n d t h i s a n d t u r n i t o n o n l y i f y o u r e q u i r e / / i t t o s a v e b y t e s . i > 0) ? "first" : ""); // We're iterating through the 'td's here but also adding in the column head text as a //data attribute in case we // want it later. You can put a conditional around this and turn it on only if you require //it to save bytes. i>0)?"first":""); //We′reiteratingthroughthe′td′sherebutalsoaddinginthecolumnheadtextasa //dataattributeincasewe //wantitlater.Youcanputaconditionalaroundthisandturnitononlyifyourequire //ittosavebytes. output .= ‘<’ . $tdElement . ’ class=“td ’ . $first . '” data-colhead=”’ . m o d e l [ 0 ] [ model[0][ model[0][j] .
'”>’ . m o d e l [ model[ model[i][$j] . ‘</’ . KaTeX parse error: Expected 'EOF', got '}' at position 24: …t . '>';` ` }̲ output .= ‘</’ . KaTeX parse error: Expected 'EOF', got '}' at position 20: …ement . '>'; }̲ output .= ‘</’ . $tableElement . ‘>’;
echo $output;
}
?>`实际的控制非常简单。真正的工作由风格来完成。让我们看看他们。
款式
我们用 CSS 样式做所有的布局工作。如前所述,这样做有很多好处。首先,尽可能多地使用 CSS 可以提高访问者的性能,因为 CSS 的呈现是浏览器固有的(而不是解释脚本,后者要慢得多)。
第二,我们可能能够更好地利用缓存。如果访问者已经查看了带有使用我们的表格样式表的表格的页面,那么这些样式就在缓存中,从而节省了大量的字节(提高了访问者的页面加载时间,降低了我们虚构公司的带宽成本)。因此,我们希望通过 CSS 做尽可能多的工作。这就是为什么 CSS 样式是我们表格控件中最大的部分。如果样式是动态组装的(从一个动态构建每个页面的系统中),我们就失去了缓存 isi 的好处,所以考虑如何构建我们的文件(HTML 和 CSS)是值得的,这样我们就可以从缓存中获得最大的好处。
注意在我们的示例站点上,CSS 样式在一个文件中。我们将它们分成几个清单,这样我们就可以对每个部分做一些解释。
所有治疗的风格
清单 16-4 到 16-6 显示了适用于表格控件所有处理的规则。如果我们添加了第三个处理,这些规则将适用于第三个处理以及到目前为止创建的两个处理。
清单 16-4 将列表元素的填充和边距设置为 0(零),并将
list-style
属性设置为 none(以避免在我们的表中得到任何不合适的项目符号)。***清单 16-4。*控制表格控件外观的 CSS
.table ul, .table li { padding: 0; margin: 0; list-style: none; }
清单 16-5 展示了我们如何为组成一个表格的各种元素设置显示模式,从而得到一个表格、行和单元格。
***清单 16-5。*为定义表格的类设置显示模式
/* * This is how we achieve our tablelike structure with these elements. * Basically we're just assigning the same attributes that the corresponding * table elements have in the 'display' property. */ .table { display: table; } .tr { display: table-row; } .td { display: table-cell; }
清单 16-6 显示了我们如何为表格控件生成的表格设置边框。
***清单 16-6。*控制表格的边框
.table, .tr, .td { border: 1px solid #CCC; border-collapse: collapse; }
款式为宽大处理
清单 16-7 到 16-24 包含了定义表格控件宽处理外观的规则。
清单 16-7 展示了我们如何设置表格的宽度和表格外侧的阴影。在这种情况下,我们为表格(名为
tableTreatment1
)的宽处理设置这些值。***清单 16-7。*设置宽处理的桌子宽度和阴影
/* * Table Treatment 1 */ .tableTreatment1 { width: 800px; -webkit-box-shadow: 2px 2px 2px #999999; -moz-box-shadow: 2px 2px 2px #999999; box-shadow: 2px 2px 2px #999999; }
清单 16-8 为宽处理设置最左边单元格的外观。特别是,它将背景设置为浅蓝色,将字体粗细设置为粗体。
***清单 16-8。*为宽处理样式化最左边单元格的内容
.tableTreatment1 .first { background: #c8dfff;
font-weight: bold; }
清单 16-9 展示了我们如何设计身体细胞的样式。为此,我们使用了一个很少使用的 CSS 选择器(
nth-child
选择器)。这样做可以让我们对每隔一行设置不同的样式,从而使表格更具吸引力,更易于阅读。我们在评论中添加了额外的解释。***清单 16-9。*塑造身体细胞
/* * By using the nth-child selector (which works in all * modern browsers), we can give our table a striped look. * Normally, you'd do this on the server side or with * JavaScript. Alternating the colors of the rows improves * readability and is (we think) more attractive. */
清单 16-10 显示了我们如何设置高亮单元格的颜色。
***清单 16-10。*改变高亮单元格的颜色
.tableTreatment1 .tableBody.tr:nth-child(odd) { background: #eef3fe; }
清单 16-11 展示了我们如何设置访问者鼠标悬停的行的颜色。同样,我们使用了
nth-child
选择器来决定突出显示哪一行。请注意,我们设计奇数行的样式,但从不设计偶数行的样式。这是因为另一个规则(在清单 16-14 中)设置了所有行的悬停状态。那么这个规则会覆盖那个规则(因为这个规则更具体)来设置奇数行的悬停状态。我们将在这组规则中反复看到这种安排,我们将在本节的最后讨论这种技术的一个常见陷阱(以及如何避免它)。***清单 16-11。*设置一行的悬停状态
.tableTreatment1 .tableBody.tr:nth-child(odd):hover { /* * We add a hover state to the odd .tr's that are also of * class .tableBody. This effect highlights the row the * visitor is hovering over with a slightly different * background color. However, since we want to maintain * the alternating row effect, this effect will be slightly * different from that on the even rows. */ background: #cad0f8; }
清单 16-12 展示了当高亮显示时,我们如何让第三列的单元格变成深绿色。注意,我们使用
nth-child
选择器来指定奇数行和特定的列(第三列)。***清单 16-12。*设置第三列的悬停颜色
.tableTreatment1 .tableBody.tr:nth-child(odd):hover .td:nth-child(3) { /* * We also use the nth-child selector to target columns.
* In this case, we target the third column of each odd row. * This rule complements the next rule, which makes the * entire 3rd column a green color but makes the alternating * rows a slightly darker green color to maintain readability. */ background: #54d543; }
清单 16-13 展示了我们如何使第三列的奇数行比第三列的偶数行稍微暗一点。我们再次使用
nth-child
选择器来指定奇数行和特定的列(第三列)。***清单 16-13。*把第三列的奇数行变成稍微暗一点的绿色
.tableTreatment1 .tableBody.tr:nth-child(odd) .td:nth-child(3) { /* * To maintain the integrity of our different-color column, * we make the highlighted cell go to a stronger green color * when the visitor hovers on that row. This rule selects only * the odd rows in the green column. */ background: #8ce981; }
清单 16-14 为正文行设置基本悬停高亮效果。同样,它实际上是为没有另外指定的行设置高亮效果,但效果是为偶数行定义悬停效果。
***清单 16-14。*为车身行设置基本悬停高亮
.tableTreatment1 .tableBody.tr:hover { background: #eeeff0; color: #1d80fd; }
清单 16-15 为第三列中的单元格设置悬停颜色,使得该列中高亮显示的单元格的背景颜色为深绿色。
***清单 16-15。*设置第三列单元格的悬停颜色
.tableTreatment1 .tableBody.tr:hover .td:nth-child(3) { background: #76f564; }
清单 16-16 使第三列变成绿色。因为将第三列的奇数行设置为较暗的绿色的规则具有更高的特异性,所以该规则将偶数行设置为较浅的绿色,这是我们用作第三列的基本颜色。
***清单 16-16。*使第三列变绿
.tableTreatment1 .tableBody.tr .td:nth-child(3) { /* * Now we make the entire third column a slightly green
* background color. Because the previous nth-child * selector has greater specificity, it will override this * rule in odd rows. */ background: #b2ebac; }
清单 16-17 显示了我们如何设置单元格的外观(不包括背景颜色),包括标题单元格和主体单元格。
***清单 16-17。*单元格样式(不包括背景色)
.tableTreatment1 .td { padding: 4px; font-size: 12px; vertical-align: middle; }
清单 16-18 显示了我们如何设置高亮单元格的前景色和背景色。前景色变成白色,背景色变成强烈的蓝色。因为第三列的悬停规则(下一个规则,在清单 16-19 中)具有更大的特异性,所以这个规则不适用于第三列。
***清单 16-18。*为除第三列之外的所有列设置悬停状态
.tableTreatment1 .tableBody .td:hover { color: white; background: #1d80fd; }
清单 16-19 展示了当访问者的鼠标悬停在第三列的单元格上时,我们如何设置该单元格的前景色和背景色。指令确保当访问者的鼠标停留在第三列的单元格上时,它们总是变成深绿色。
***清单 16-19。*第三列单元格的悬停规则
.tableTreatment1 .tableBody:hover .td:nth-child(3):hover { color: white; background: #12ba00 !important; }
清单 16-20 控制标题行的外观。它将文本设置为白色和粗体,设置深灰色文本阴影,并设置浅灰色背景色。此外,对于大多数浏览器,它使用 SVG 图像来设置页眉的背景图像。最后,它为大多数浏览器设置了渐变效果。最终效果是深色渐变背景上的白色文本。
***清单 16-20。*宽处理的表头造型
.tableTreatment1 .tableHead { color: white; font-weight: bold; text-shadow: 0 0 3px #333; background: #aebcbf; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw
Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2FlYmNiZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUw JSIgc3RvcC1jb2xvcj0iIzZlNzc3NCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUxJSIgc3RvcC1j b2xvcj0iIzBhMGUwYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMw YTA4MDkiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9 IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); background: -moz-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #aebcbf), color-stop(50%, #6e7774), color-stop(51%, #0a0e0a), color-stop(100%, #0a0809)); background: -webkit-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -o-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -ms-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: linear-gradient(to bottom, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#aebcbf', endColorstr='#0a0809', GradientType=0); }
清单 16-21 只是将标题行中每个单元格的内容居中——这很简单,但对标题中的内容对齐很有必要。
***清单 16-21。*将标题行的单元格居中
.tableTreatment1 .tableHead .td { text-align: center; }
清单 16-22 显示了当访问者使用 IE9 时,我们在哪里关闭我们在清单 16-21 中创建的过滤器。IE9 不需要那个滤镜,所以我们在这条规则里把它关了。
***清单 16-22。*为 IE9 关闭标题中的过滤器
.ie9 .tableTreatment1 .tableHead { filter: none; }
清单 16-23 显示了当访问者使用 IE8 时,我们如何关闭我们在清单 16-21 中创建的过滤器和背景。Internet Explorer 需要为每个单元格设置背景,而不是为整行设置背景,所以我们将在清单 16-24 中这样做。
***清单 16-23。*关闭 IE8 的滤镜和背景
.ie8 .tableTreatment1 .tableHead { filter: none; background: none; }
清单 16-24 显示了当访问者使用 IE8 时,我们如何为标题单元格设置背景和过滤。IE8 不会对行使用背景和过滤器定义,所以我们必须在单元格上设置这些值。
***清单 16-24。*为 IE8 中的标题单元格设置背景
.ie8 .tableTreatment1 .tableHead .td { background: #aebcbf; filter: progid:dximagetransform.microsoft.gradient(startColorstr='#aebcbf', endColorstr='#0a0809', GradientType=0); }
款式为窄幅处理
窄处理获得的样式与定义宽处理外观的样式有很大不同。首先,窄处理为控件数据中的每个数组创建了一组行,而不是单个行。此外,第二种处理没有突出悬停效果。由于触摸屏(无论是手机还是冰箱)没有鼠标指针浮动,设置悬停效果没有意义。因此,狭义处理的规则要简单得多。清单 16-25 到 16-32 显示了定义我们的表格控件的窄处理的规则。
清单 16-25 显示了我们如何将窄表格的宽度设置为 320 像素。
***清单 16-25。*设定窄治疗的宽度
/* * Table Treatment 2 */ .tableTreatment2 { width: 320px; }
清单 16-26 显示了当我们使用窄处理时,我们如何关闭 HTML 中所有元素的边框。
***清单 16-26。*关闭窄边框处理
.tableTreatment2 .table, .tableTreatment2 .tr, .tableTreatment2 .td { border: none; }
清单 16-27 显示了我们如何关闭标题内容的显示,因为我们在窄处理中没有使用标题单元格。
***清单 16-27。*关闭割台单元进行窄处理
.tableTreatment2 .tableHead { display: none; }
清单 16-28 显示了我们如何在表格控件的窄处理中将一行的宽度设置为 200 像素。
***清单 16-28。*将行宽设置为 200 像素
.tableTreatment2 .tr { width: 200px; }
清单 16-29 展示了我们如何在表格控件的窄处理中设计单元格的样式。
***清单 16-29。*对狭窄治疗区内的细胞进行造型
.tableTreatment2 .td { display: block; float: none; padding: 5px; }
清单 16-30 展示了我们如何对表格控件的窄处理的每个块中的第一个单元格进行样式化。我们在宽处理中使用与标题行相同的样式。
first-child
选择器让我们只指定窄处理中每个块的第一个单元格。***清单 16-30。*对每个区块内的第一个单元格进行窄处理
.tableTreatment2 .td:first-child { /* * By using the first-child selector, we can target * the first TD, which is a relevant pivot point for our data. */ color: white; font-weight: bold; text-shadow: 0 0 3px #333; background: #aebcbf; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2FlYmNiZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUw JSIgc3RvcC1jb2xvcj0iIzZlNzc3NCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUxJSIgc3RvcC1j b2xvcj0iIzBhMGUwYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMw YTA4MDkiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9 IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); background: -moz-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #aebcbf), color-stop(50%, #6e7774), color-stop(51%, #0a0e0a), color-stop(100%, #0a0809)); background: -webkit-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -o-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -ms-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: linear-gradient(to bottom, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#aebcbf', endColorstr='#0a080 9', GradientType=0); }
清单 16-31 展示了我们如何在单元格的常规内容前插入
data-colhead
属性的值、一个冒号和一个空格。我们还将所有插入的内容设置为粗体和中深灰色。这种技术为每个单元格提供了相当于标题的内容。***清单 16-31。*在窄处理中插入相当于单元格标题的内容
.tableTreatment2 .td:before { /* * We're able to leverage the content: attr() again as we did * in our link control. Since our control has stored the * value of the column in the 'td' we can just get it from there. */ content: attr(data-colhead) ": "; font-weight: bold; color: #777; }
清单 16-32 显示了我们如何覆盖每个块中第一行的文本插入。因为我们不想在标题前以冒号和空格结束,所以我们需要将第一个设置为空字符串(" ")。
***清单 16-32。*确保第一行没有无意义字符
.tableTreatment2 .td:first-child:before { /* * Since we don't need the header data for the * first TD, we override it so we don't get the ': '. */ content: ""; }
添加媒体查询
我们认为这种控制很适合媒体询问。对于使用窄设备的访问者,很自然地会想到将布局从宽改为窄。如果你想给你的表格控件添加媒体查询,清单 16-33 中的规则显示了如何做。我们不单独描述它们,因为它们都与我们已经描述过的规则非常相似。当您使用这组规则时,您可以将它们添加到支持表格控件的 CSS 文件中的任何位置。
/* Mobile Layout */ @media only screen and (max-width: 767px) { .tableTreatment1 { width: 320px; } .tableTreatment1 .table, .tableTreatment1 .tr, .tableTreatment1 .td { border: none; } .tableTreatment1 .tableHead { display: none; } .tableTreatment1 .tr { width: 200px; } .tableTreatment1 .td { display: block; float: none;
padding: 5px; } .tableTreatment1 .td:first-child { color: white; font-weight: bold; text-shadow: 0 0 3px #333; background: #aebcbf; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodH RwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcH Jlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncm FkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3 Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2FlYmNiZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9Ij UwJSIgc3RvcC1jb2xvcj0iIzZlNzc3NCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUxJSIgc3RvcC 1jb2xvcj0iIzBhMGUwYSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9Ii MwYTA4MDkiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdG g9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); background: -moz-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #aebcbf), color-stop(50%, #6e7774), color-stop(51%, #0a0e0a), color-stop(100%, #0a0809)); background: -webkit-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%) ; background: -o-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: -ms-linear-gradient(top, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); background: linear-gradient(to bottom, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#aebcbf', endColorstr='#0a 0809', GradientType=0); } .tableTreatment1 .td:before { content: attr(data-colhead) ": "; font-weight: bold; color: #777; } .tableTreatment1 .td:first-child:before { content: ""; } }
这些规则产生了一个类似于狭义处理的表。然而,媒体查询中的规则实际上修改了宽处理(
tableTreatment1
)。结果是一个具有窄处理结构和宽处理配色方案的表格。图 16-4 显示了结果。***图 16-4。*使用表格控件进行媒体查询的结果
对我们来说,为表格控件编写 CSS 规则很有趣。我们不常使用那些不常见的选择器,所以找个地方展示它们是如何工作的是很好的。许多表可能不需要这种复杂程度;根据我们的经验,将中间的一列变成不同的颜色是不常见的。然而,当你需要这样做的时候,选择符可以让你指定一个特定的孩子,甚至是孩子中的孩子。正如我们所说,这很有趣,但我们也很奇怪。
**小心时连不是真的连。**我们顺便提到偶数行没有被指定为偶数。相反,我们为所有行设置背景,然后覆盖奇数行的背景。我们可以使用
even
说明符来补充odd
说明符,但是它会添加我们不需要添加的字节。此外,最好避免不必要的说明符。除了节省字节,您可能会遇到这样的情况:使用看似互补的说明符,然后发现有些奇怪的情况与它们都不匹配。所以最好设定一个基础待遇,指定例外情况。在这种情况下,奇数和偶数就可以了。当存在两种以上的情况时,就会出现问题,例如序列中第一项的附加值以及偶数和奇数的值。总结
在这一章中,我们已经展示了如何开发和使用一个表格控件。我们展示了,如果你使用响应式设计,你不能使用通常的表格元素(
table
、tr
和td
)。然后我们展示了如何通过在一个div
元素中创建一组列表来制作一个没有这些元素的表格。之后,我们展示了如何使用代码(本例中是 PHP,但也可以是 Java 或 C#或任何支持 web 的语言)将数据转换成 HTML,我们可以对其进行样式化,使其成为表格。最后,我们描述了将这些列表转换成表格中的行和单元格的每种样式。在这个过程中,我们演示了如何选择表中的单个行和单元格。我们用这种效果来改变前景和背景的颜色。但是,您也可以使用这些选择器来创建一个包含帮助内容或更多产品细节或任何您能想到的内容的
iframe
元素,然后将iframe
元素放在高亮显示的单元格附近。我们确信选择器还有其他的用途,可以让你指定一个特定的孩子(或者一组孩子,在even
和odd
说明符的情况下),但是我们会把它们留给你自己去想象。十七、选项卡控件
我们都在网上见过很多标签。有些还可以。有些发臭。很自然,我们希望制作一个不会发臭的选项卡控件(希望这比“好”要好),这样我们就可以在各种内容中重用它,同时确保它看起来不错,运行良好。
网站上的标签经常令人讨厌的一个原因是它们不能追踪访问者的位置。你有没有去过一个页面,导航到第三个标签找到你想要的,去了另一个网站,然后回到有标签的网站,却发现你又在第一个标签上?我们有,我们不喜欢它。更糟糕的是,如果访问者刷新页面,一些标签方案会跳回到第一个标签。真烦人。在一个相关的问题上,我们也希望标签,我们(和我们的访问者)可以链接。
为了在设计上有一点灵活性,我们希望标签沿着内容的顶部运行(即水平标签),标签沿着内容的左侧运行(即垂直标签)。在我们看来,一些内容似乎用水平或垂直标签更有意义。此外,我们希望我们的页面设计者(将所有这些控件放在一起制作网页的人)可以选择水平或垂直标签。因此,我们在一个对照中有两个处理(变异)。自然,我们希望我们的标签看起来不错。标签并不是有史以来最吸引人的网页设计元素,但这并不意味着它们必须是包含纯文本的方形盒子。因此,我们希望标签有圆角,渐变背景,文本水平和垂直居中。当一个或多个标签包含多行文本时,文本必须保持居中。最重要的是,我们希望标签在收缩和展开以适应它们的内容时是动态的。
注我们无法在一本书里展示一个动画效果。要以动画方式查看选项卡的大小变化,请在我们的示例站点上使用选项卡控件:
[
www.clikz.us/BookExamples/tabControl.php](http://www.clikz.us/BookExamples/tabControl.php)
最后但同样重要的是,我们希望标签表现良好。不幸的是,许多选项卡控件使用表格。正如我们在前一章所讨论的,表有许多性能问题。此外,我们严格要求表格只能用于表格内容。然而,标签是表格内容。那么,我们如何在不使用那些糟糕的表格元素的情况下获得类似表格的功能呢?幸运的是,CSS 提供了一种方法——通过
display
属性。正如你将在本章后面的 CSS 清单部分看到的,我们使用display:table
将列表转换成表格,然后使用display:table-cell
将列表项转换成表格单元格。这种技术提供了更好的性能,因为浏览器的呈现引擎在开始呈现每个单元格之前不会等待一整行(或者更糟,整个表格)到达。这并没有很大的区别,除非您有很多表,或者更糟,表中有表。然而,这些小问题累积起来会导致糟糕的性能,因此我们尽可能地防止所有这些小的性能问题。正如我们在别处提到的,web 开发人员获得了更显著的性能提升。从数据库中检索数据并将其传递给一个函数(控件)要比每次需要时自己制作标签容易得多。此外,控件确保一致性,这在大多数网站上都是一个重要的问题。现在我们有了一个列表,列出了我们希望标签做的事情(也就是说,我们对控件有了要求),我们可以继续描述如何让所有这些工作起来。在我们阅读这一章的时候,我们还会探索一些技术细节,我们必须实现这些细节才能使一切正常工作。不过,现在让我们先来看看输出。图 17-1 显示了我们的选项卡控件的水平处理。
***图 17-1。*选项卡控件的水平处理
为了多样化,水平处理使用白色背景,而垂直处理使用黑色背景。你可以很容易地使用其他颜色。图 17-2 显示了选项卡控件的垂直处理。
***图 17-2。*选项卡控件的垂直处理
HTML
像往常一样,控件不直接包含 HTML。相反,PHP 函数在页面加载时创建 HTML。不过,查看控件创建的 HTML 来为脚本 (JavaScript)和样式(CSS)信息提供上下文还是很有帮助的。HTML 在水平和垂直处理之间不会改变;我们完全通过 CSS 来控制外观上的差异。
HTML 由一个 section 元素组成,该元素有两个
ul
子元素。第一个列表的li
孩子,其 class 属性包括tabTriggerLI
,持有标签。class 属性包含activeTab
的第一个列表的li
子列表持有当前打开的选项卡的标签。第二个ul
元素包含选项卡的内容(无论当前打开还是关闭)。class 属性包含tabContentLI
的li
元素包含选项卡的内容。class 属性包含showTab
的li
元素保存当前打开的选项卡的内容。清单 17-1 显示了在图 17-1 和 17-2 中显示的选项卡控件的 HTML,打开到第一个选项卡。***清单 17-1。*标签页的 HTML】
<section data-item="tab" class="tabWrap tabTreatment1" id="tabID"> <ul class="tabTriggerUL"> <li class="tabTriggerLI activeTab"> <a href="#tab1" class="tabTriggerA">This is a Tab</a> </li> <li class="tabTriggerLI"> <a href="#tab2" class="tabTriggerA">The Trigger for a <br> Long Tab Two</a> </li> <li class="tabTriggerLI"> <a href="#tab3" class="tabTriggerA">Don't Forget Me</a> </li> <li class="tabTriggerLI"> <a href="#tab4" class="tabTriggerA">I Like Being a Tab</a> </li> </ul> <ul class="tabContentUL" style="height: 215px; "> <li class="tabContentLI showTab" data-tabid="#tab1"> <p> Skateboard banh mi direct trade fanny pack mixtape, pork belly art party. Dreamcatcher wes anderson raw denim kogi gastropub. </p> <p> Thundercats ennui carles iphone, pour-over photo booth quinoa leggings stumptown PBR fanny pack cliche gluten-free. </p> <p> Small batch tofu gluten-free, vinyl you probably haven't heard of them typewriter umami viral DIY four loko aesthetic. </p> </li> <li class="tabContentLI" data-tabid="#tab2"> <p> Carles viral yr, williamsburg letterpress ethnic gluten-free aesthetic american apparel ennui chambray polaroid you probably haven't heard of them. </p> <p>
` Scenester hoodie tattooed food truck tofu, selvage blog sriracha polaroid hella keytar
before they sold out +1.
-
’;
Hella 8-bit viral master cleanse salvia shoreditch. Leggings banksy mustache, godard VHS
truffaut mixtape ethnic umami gluten-free occupy kale chips skateboard mcsweeney’s small batch.
Swag carles terry richardson, chillwave sustainable pickled high life keffiyeh single-
origin coffee fanny pack kogi.
Carles farm-to-table stumptown, pitchfork williamsburg wes anderson whatever sartorial.
You probably haven’t heard of them kogi kale chips, gluten-free scenester leggings pitchfork
authentic next level raw denim mcsweeney’s dreamcatcher umami.
Swag carles terry richardson, chillwave sustainable pickled high life keffiyeh single-
origin coffee fanny pack kogi.
Carles farm-to-table stumptown, pitchfork williamsburg wes anderson whatever sartorial.
You probably haven’t heard of them kogi kale chips, gluten-free scenester leggings pitchfork
authentic next level raw denim mcsweeney’s dreamcatcher umami.
`数据
在描述控件核心的函数时,我们提到了
tabControl
函数的第一个参数是一个模型对象。因为我们使用 PHP,所以它必须是一个 PHP 对象。在这种情况下,它包含一个 JSON 对象。JSON 对象包含一组逗号分隔的值,每个选项卡对应一个值。每个值都包含一个逗号分隔的列表,其中包含每个选项卡的详细信息。换句话说,数据对象由外部逗号分隔列表中的逗号分隔列表组成。提示
tabcontent
值包含 HTML。您可以使用这种机制将链接、图像或其他 HTML 元素插入到选项卡的内容中。清单 17-2 显示了我们用来创建标签控件的数据对象。
***清单 17-2。*为我们的样本选项卡控件提供数据的对象
<?php $tabObj = '[ { "triggertext" : "This is a Tab", "triggerid" : "#tab1", "tabcontent" : "<p>Skateboard banh mi direct trade fanny pack mixtape, pork belly art party. Dreamcatcher wes anderson raw denim kogi gastropub. </p><p>Thundercats ennui carles iphone, pour-over photo booth quinoa leggings stumptown PBR fanny pack cliche gluten-free. </p><p>Small batch tofu gluten-free, vinyl you probably haven\'t heard of them typewriter umami viral DIY four loko aesthetic.</p>" }, { "triggertext" : "The Trigger for a <br>Long Tab Two", "triggerid" : "#tab2", "tabcontent" : "<p>Carles viral yr, williamsburg letterpress ethnic gluten-free aesthetic american apparel ennui chambray polaroid you probably haven\'t heard of them. </p><p>Scenester hoodie tattooed food truck tofu, selvage blog sriracha polaroid hella keytar before they sold out +1\. </p>" }, { "triggertext" : "Don\'t Forget Me", "triggerid" : "#tab3", "tabcontent" : "<p>Hella 8-bit viral master cleanse salvia shoreditch. Leggings banksy mustache, godard VHS truffaut mixtape ethnic umami gluten-free occupy kale chips skateboard mcsweeney\'s small batch.</p><p>Swag carles terry richardson, chillwave sustainable pickled high life keffiyeh single-origin coffee fanny pack kogi.</p><p>Carles farm-to-table stumptown, pitchfork williamsburg wes anderson whatever sartorial. You probably haven\'t heard of them kogi kale chips, gluten-free scenester leggings pitchfork authentic next level raw denim mcsweeney\'s dreamcatcher umami. </p>" }, { "triggertext" : "I Like Being a Tab", "triggerid" : "#tab4", "tabcontent" : "<p>Swag carles terry richardson, chillwave sustainable pickled high life keffiyeh single-origin coffee fanny pack kogi.</p><p>Carles farm-to-table stumptown, pitchfork williamsburg wes anderson whatever sartorial. You probably haven\'t heard of them kogi kale chips, gluten-free scenester leggings pitchfork authentic next level raw denim mcsweeney\'s dreamcatcher umami. </p>" } ]'; ?>
控制
毫无疑问,你现在已经习惯了(除非你因为某种原因跳到了这一章——如果你直接跳到了这里,请阅读第十章;它解释了我们为什么要做控件),实际的控件是一个 PHP 函数(本例中称为
tabControl
)。该函数有三个参数,如表 17-1 所述。tabControl
函数创建一个输出变量,它可以将控件生成的 HTML 写入其中。然后,它将 section 元素的开始标记及其所有属性(包括该选项卡控件的标识符)添加到输出变量中。接下来,该函数将保存选项卡控件标签的ul
元素的开始标签写入输出变量。然后一个for
循环将列表的内容写入输出变量(也就是说,它将包含标签的li
元素写入输出变量)。在写li
元素时,控件插入数据,包括属性和它们的值。然后,该函数关闭第一个列表,并启动第二个列表,其中包含选项卡的内容。另一个for
循环将包含选项卡内容的li
元素(及其属性)写入输出变量。然后,该函数将第二个列表的结束标记和 section 元素写入输出变量。最后,该函数将输出变量的内容(现在包含我们的 HTML,所有数据都在正确的位置)回显到浏览器。在浏览器中,我们的 CSS 和 JavaScript 可以与 HTML 交互,生成最终的选项卡控件供访问者使用。注意
id
属性的插入支持链接到特定的标签页,并使各个标签页出现在浏览器的历史记录中,这样访问者就可以返回到特定的标签页(或者在刷新页面时返回到同一个标签页)。基本上,id
让浏览器记住每个标签作为一个单独的位置。清单 17-3 显示了
tabControl
函数。***清单 17-3。*tab control 功能
<?php function tabControl($model, $id, $treatment) { $output = '<section data-item="tab" class="tabWrap ' . $treatment . '" id="' . $id . '">'; $output .= ' <ul class="tabTriggerUL">'; for ($i = 0; $i < sizeof($model); $i++) { $output .= ' <li class="tabTriggerLI">'; $output .= ' <a href="' . $model[$i] -> triggerid . '" class="tabTriggerA">' . $model[$i] -> triggertext . '</a>'; $output .= ' </li>'; } $output .= ' </ul>'; $output .= ' <ul class="tabContentUL">'; for ($j = 0; $j < sizeof($model); $j++) { $output .= ' <li class="tabContentLI" data-tabid="' . $model[$j] -> triggerid . '">' . $model[$j] -> tabcontent . '</li>'; }
` o u t p u t . = ′ < / u l > ′ ; output .= ' </ul>'; output.=′ </ul>′; output .= ‘’;echo $output;
}
?>`创建选项卡控件
要创建选项卡控件,必须调用
tabControl
函数。如前所述,制作选项卡控件的 PHP 函数有三个参数:数据、每个选项卡控件的唯一标识符和处理方式(水平或垂直)。标识符"tabTreatment1"
表示水平标签控件;"tabTreatment2"
表示垂直选项卡控件。然后,脚本会将选项卡放置在页面中。清单 17-4 展示了我们是如何创建标签控件的,这些控件在我们的示例站点和本章中被用作示例。***清单 17-4。*创建我们的样本标签控件
`<?php tabControl(json_decode($tabObj), "tabID", "tabTreatment1"); ?>
<?php tabControl(json_decode($tabObj), "tabID2", "tabTreatment2"); ?> `JavaScript
我们的很多控件都不使用 JavaScript。在选项卡控件中,它用于一点交互性:当访问者点击它的触发器时,让右边的选项卡出现。我们还使用 JavaScript 让选项卡控件在 IE7 中正确呈现。在本例中,我们创建了一个名为
tab
的 jQuery 插件。该插件使访问者能够通过浏览器的后退按钮、浏览器的历史功能或特定选项卡的链接来打开特定选项卡。它还向每个选项卡添加了一个 click 事件,从而可以打开访问者单击的选项卡。它还控制当不同选项卡的内容大小不同时触发的动画效果。我们没有在这里详细描述这个插件,而是在整个插件中进行了广泛的注释。这些注释构成了一个很长的列表,但是它们也将描述放到了一个更有意义的上下文中。清单 17-5 显示了标签控件的 jQuery。
***清单 17-5。*我们选项卡控件的 jQuery 插件
`;(function($) {
$.fn.extend({
//name of our plug-in
tab : function() {return this.each(function() {
// Declare some pointers relative to the element passed
// into the plug-in.
var tab = $(this), hash = window.location.hash,
// selector to find
our targeted tab content
tabContentUL = tab.find(“.tabContentUL”),
// Grabs all the .tabContentLI’s into a collection.
tabContentLIs = tabContentUL.find(“.tabContentLI”),
// Grab a collection of tab trigger anchor tags.
tabTriggers = tab.find(“.tabTriggerA”), tabTriggerLIs = tab.find(“.tabTriggerLI”);
// If a user has bookmarked this page, we want to open the
// tab he had open when bookmarking. First we’ll check to see
// whether there’s a hash left by using one of the tab
// triggers.
if (hash) {
var count = 0;
tabTriggers.each(function checkHash() {
// Because this variable is scoped within a function
// we can use ‘t’ again to represent each tab trigger
// as we enumerate through the collection.
var t = $(this);
if (t.attr(“href”) == hash) {
// if the href matches the hash, add the activeTab
// class to the that triggers parent LI
t.parents(“.tabTriggerLI”).addClass(“activeTab”),
// Then find the matching content tab and add
// the showTab class to that, displaying that tab.
currentTab = findTabContentLI(hash);
currentTab.addClass(“showTab”);
//Set the height of the tabContentUL for the bookmarked
// contentTab.
tabContentUL.height(currentTab.outerHeight(true));
} else {
// Add 1 to the count if there isn’t a match
// to the hash. We’ll use this later to trigger
// the first tab if we don’t find a match for any
// of the elements.
count++;
}
});
// In case our page has changed since the user bookmarked it
// or if there are two tab controls on a page with varying
// tab trigger names, we’ll handle the case where there’s no
// matches and display the first tab. So if the count shows as
// many unmatched tabTriggers as there are tabTriggers in
// total, we’ll show the first tab.
if (count == tabTriggers.length) {
showFirstTab();
}} else {
// If there’s no hash, show the first tab
showFirstTab();
// Add a click event to the tab targets.
$(this).find(“.tabTriggerA”).click(function tabTrigger() {
// Captures the element clicked and makes it a jQuery element.
// The $(this) here is scoped to only the .tabTriggerA that
// was clicked.
var t = $(this),
// We’ll resuse the href as the identifier
// for our tab selection
tabId = t.attr(“href”),
// Find the matching .tablContentLI with the same id attribute
// as the href from the target selected.
targetContentTab = findTabContentLI(tabId);
// Set all the tab content areas back to the default of
// display: none and opacity:0 by removing the showTab class
// from all of them.
tabContentLIs.removeClass(“showTab”);
// The same as above: Remove all the ‘activeTab’ classes from
// the tabTriggers to reset them.
tabTriggerLIs.removeClass(“activeTab”);
// Add a class to the LI containing the clicked tab trigger.
// We’ll use this to style the active trigger.
t.parents(“.tabTriggerLI”).addClass(“activeTab”);// For our progressive enhancement, we’re going to use CSS3
// transitions to handle animating the height of our tab
// content wrap to match its contents, as well as animating
// the opacity to let the content fade in after the height
// animation is complete. However if the CSS3 animation isn’t
// available we’ll get the same effect by using Javascript.
if (Modernizr.cssanimations) {tabContentUL.height(targetContentTab.outerHeight(true));
targetContentTab.addClass(“showTab”);
} else {
tabContentUL.animate(
// Declare what property to animate. In this case we’re
// animating the height of the .tabContentUL to the same
// height as the targetContentTab plus padding (that’s the
// outerHeight()).
{
“height” : targetContentTab.outerHeight() + “px”
},
// how long to complete the animation
300,
// callback after animation is complete.
function showTab() {
targetContentTab.addClass(“showTab”);
});
}
// Because IE still has some trouble being in compliance, we’ll
// trigger a JS solution to allow for our tabs to be any height
// and still retain the tab style metaphor by being the same
// height.
if($(“html”).hasClass(“ie”)){
equalizeTriggerHeights();
}
// We’ll find the tallest triggerLI and set the remaining LIs to
// that height.
function equalizeTriggerHeights(){
var maxHeight = 0;
tabTriggerLIs.each(function(){
var t = ( t h i s ) ; v a r t H e i g h t = t . h e i g h t ( ) ; i f ( t H e i g h t > m a x H e i g h t ) m a x H e i g h t = t H e i g h t ; / / A s l o n g a s w e ′ r e i n h e r e , w e ′ l l v e r t i c a l l y c e n t e r / / t h e t e x t i n s i d e t h e t r i g g e r L I s . W e ′ l l a c c o m p l i s h / / t h i s c e n t e r i n g b y p o s i t i o n i n g t h e t e x t 50 / / t o p , w h i c h p u t s t h e t o p o f t h e t e x t b o x i n t h e m i d d l e / / o f t h e v e r t i c a l h e i g h t o f o u r L I . B u t s i n c e t h a t w o u l d / / l o o k t o o l o w , w e ′ l l g i v e t h e t e x t a n e g a t i v e m a r g i n e q u a l / / t o 1 / 2 i t s h e i g h t ; t h a t s h o u l d d o t h a t t r i c k . H o w e v e r , / / I E 7 d o e s n ′ t a l l o w t h i s t r i c k , s o w e ′ l l d i s a b l e i t f o r / / t h a t b r o w s e r . T h e r e a s o n w e d o n ′ t u s e C S S t o s e t t h e / / m a r g i n i s t h a t w e d o n ′ t k n o w h o w h i g h t h e t a b w i l l b e , / / b e c a u s e t h e t a b m a y h a v e m o r e t h a n o n e l i n e o f c o n t e n t . i f ( ! (this); var tHeight = t.height(); if(tHeight > maxHeight){ maxHeight = tHeight; } // As long as we're in here, we'll vertically center // the text inside the trigger LIs. We'll accomplish // this centering by positioning the text 50% from the // top, which puts the top of the text box in the middle // of the vertical height of our LI. But since that would // look too low, we'll give the text a negative margin equal // to 1/2 its height; that should do that trick. However, // IE7 doesn't allow this trick, so we'll disable it for // that browser. The reason we don't use CSS to set the // margin is that we don't know how high the tab will be, // because the tab may have more than one line of content. if(! (this); vartHeight=t.height(); if(tHeight>maxHeight) maxHeight=tHeight; //Aslongaswe′reinhere,we′llverticallycenter //thetextinsidethetriggerLIs.We′llaccomplish //thiscenteringbypositioningthetext50 //top,whichputsthetopofthetextboxinthemiddle //oftheverticalheightofourLI.Butsincethatwould //looktoolow,we′llgivethetextanegativemarginequal //to1/2itsheight;thatshoulddothattrick.However, //IE7doesn′tallowthistrick,sowe′lldisableitfor // thatbrowser.Thereasonwedon′tuseCSStosetthe //marginisthatwedon′tknowhowhighthetabwillbe, //becausethetabmayhavemorethanonelineofcontent. if(!(“html”).hasClass(“ie7”)){
var tabTrigger = t.find(“.tabTriggerA”);
tabTrigger.css(“margin-top”, -(tabTrigger.outerHeight() / 2));
}else {
t.find(“.tabTriggerA”).css(“position”, “static”);
// There’s a display bug when IE7 first loads, we’ll
// counteract that here.
setTimeout(function(){ie7Fix();}, 100);
}
})
tabTriggerLIs.height(maxHeight);}
function ie7Fix(){
var showTab = tab.find(“.showTab”);
showTab.css(“display” , “block”);
tab.find(“.tabContentUL”).height(showTab.outerHeight(true));
}
// Finds the matching content based on the selector and returns
// the element.
function findTabContentLI(selector) {
return selectedTabContent;
}// Shows the first tab in the series.
function showFirstTab() {
var currentTab = tabContentLIs.eq(0);
currentTab.addClass(“showTab”);
tabContentUL.height(currentTab.outerHeight(true));
tabTriggers.eq(0).parents(“.tabTriggerLI”).addClass(“activeTab”);
}});
}
});})(jQuery);`
设计选项卡控件的样式
我们制作控件的标准范例是为所有处理制作一组通用的 HTML 元素;我们再次接受了选项卡控件的范例。因此,不同治疗之间的差异完全取决于 CSS。
在选项卡控件的情况下,我们有两种处理方式:水平(由名称包含
tabTreatment1
标识符的类表示)和垂直(由名称包含tabTreatment2
标识符的类表示)。除了适用于一种或另一种处理的规则之外,选项卡控件还包括一些适用于所有处理的规则。让我们从那些常见的规则开始。所有治疗的风格
一些规则适用于选项卡控件的所有处理。目前我们有两种处理方式(横向和纵向)。如果我们要开发额外的治疗方法,这些规则也将适用于这些治疗方法。正如我们对其他复杂控件所做的那样,我们将分别描述每个规则。我们还在整个规则中加入了注释。
一般原则是将选项卡控件中的列表转换成表格。表格会自动在可用区域展开标签,并很好地将标签居中。所以那些表格元素确实有用处。
注意在我们的示例站点上,CSS 样式在一个文件中。我们将它们分成单独的列表,以便解释每条规则。
清单 17-6 显示了
tabWrap
类,它为选项卡控件元素提供了一个包装器。该规则除了注释之外没有其他内容。它的存在是为了让其他规则(比如tabWrap ul
,我们将在清单 17-7 中看到)使用它作为后代说明符的一部分,并为 JavaScript 提供一个钩子,以便在制作选项卡控件动画时使用。因此,它没有工作内容,只有评论。***清单 17-6。*设计标签控件的外包装
`/*
* These rules define the generic attributes that all instances of our tab controls have.
/
.tabWrap {
/
* In order to get the tabs to fit across the whole
* control and have the text inside the tab triggers
* be vertically centered, we use display:table and
* display:table-cell to set up a faux table, with the
* UL element being the table and the LI elements
* being the cells in the table.
*//*
* This class handles making the content tab
* visible and thus enables our animations.
*/
}`清单 17-7 展示了我们如何在选项卡控件中设计两个列表的样式。我们将填充和边距值设置为零(0),并将
position
属性设置为relative
。我们将把列表中其他元素的position
属性设置为absolute
,以控制选项卡及其内容在选项卡控件中的位置。***清单 17-7。*标签控件内的列表样式
.tabWrap ul { padding: 0; margin: 0; position: relative; }
清单 17-8 展示了我们如何在每个列表中设置列表项的样式。我们将填充和边距设置为 0。我们还将
list-style
属性设置为none
,这样我们就不会在输出中得到项目符号。***清单 17-8。*标签控件内列表项的样式
.tabWrap li { padding: 0; margin: 0; list-style: none; }
清单 17-9 显示了我们如何将包含标签的列表转换成表格。因为我们将宽度设置为 100 %,所以选项卡控件现在填充了它的可用空间。
***清单 17-9。*把列表变成表格
.tabWrap .tabTriggerUL { display: table; width: 100%;
}
清单 17-10 显示了我们如何将每个标签转换成一个表格单元格。我们还设置填充值以及水平和垂直居中。
***清单 17-10。*将列表项变成表格单元格
.tabWrap .tabTriggerLI { display: table-cell; padding: 5px 20px 2px 20px; text-align: center; vertical-align: middle; }
清单 17-11 显示了我们如何将一个锚元素设计成一个标签。我们确保锚元素没有下划线(通过移除任何文本修饰),并将锚元素变成块元素而不是行内元素。
***清单 17-11。*标签标签的文本样式
.tabWrap .tabTriggerA { text-decoration: none; display: block; }
清单 17-12 显示了我们如何设计包含标签内容的列表项的样式。我们确保内容是一个块,设置一个细边框,并将
position
属性的值设置为relative
(我们将li
元素的position
属性的值设置为absolute
)。我们还将overflow
属性的值设置为hidden
,以清除浮动。***清单 17-12。*样式内容列表
.tabWrap .tabContentUL { display: block; border: 1px solid; position: relative; overflow: hidden; }
清单 17-13 显示了我们如何设计标签的内容。它包括一些值得注意的因素。首先,我们将
opacity
属性的值设置为 0,这使得内容不可见。当访问者选择一个选项卡时,我们使用一个过渡效果将opacity
属性的值设置为 1,这使得内容可见。过渡效果提供了我们的动画。对于不支持不透明的浏览器,我们将visibility
属性的值设置为hidden
。当访问者点击标签时,我们将visibility
属性的值设置为visible
。为了定位内容,我们将position
属性的值设置为absolute
,并将top
和left
属性的值设置为 0。我们还指定了各种浏览器的过渡效果。最后,我们将z-index
属性的值设置为 1。在下一个规则中,我们将把 visible 选项卡的z-index
属性的值设置为 2,确保 visible 选项卡位于顶部。***清单 17-13。*内容区样式
.tabWrap .tabContentLI { /* * We're starting with an opacity of 0 as the
* default state, and when the tab content is triggered * by the tab trigger, it will animate into 100% opacity. * We're also setting the visibility to hidden and changing * that to a visibility of visible on trigger selection * to accommodate browsers that don't handle opacity. */ -webkit-opacity: 0; -moz-opacity: 0; opacity: 0; padding: 20px; position: absolute; visibility: hidden; top: 0; left: 0; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -ms-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; z-index: 1; }
清单 17-14 展示了我们如何设计活动(或当前)标签的样式。正如我们在前面的规则中所讨论的,非活动选项卡的
opacity
属性的值是 0,非活动选项卡的visibility
属性的值是hidden
,非活动选项卡的z-index
属性的值是 1。要使选项卡可见,此规则会更改所有这些设置。它将opacity
属性的值设置为 1,将visibility
属性的值设置为visible
,并将z-index
的值设置为 2。我们设置了可见性和不透明性,以适应不处理不透明性的浏览器。这些浏览器失去了与opacity
属性相关的动画效果,但是由于可见性的设置,标签可以正常工作。结果是一个可见的选项卡。***清单 17-14。*对活动标签进行样式化
.tabWrap .showTab { -webkit-opacity: 1; -moz-opacity: 1; opacity: 1; z-index: 2; visibility: visible; }
横向处理的样式
现在我们来看看应用于选项卡控件水平处理的样式。值
tabTreatment1
表示水平处理。我们从设置选项卡控件的宽度开始。清单 17-15 显示了我们如何为水平处理设置标签控件的宽度。
***清单 17-15。*设置横向处理的宽度
/* * Here we're making style choices for the tabTreatment1.
*/ .tabTreatment1 { width: 625px; }
清单 17-16 展示了我们如何在水平处理的选项卡控件中设计实际的选项卡(每个选项卡标签周围的结构)。在这条规则中,我们规定了非活动选项卡的外观。当一个选项卡被激活时,我们会覆盖其中的一些值。这里,我们指定圆角,并为每个选项卡创建渐变。我们还设置了边框和背景色。因为我们必须考虑许多不同的浏览器,这就变成了一个相对较长的列表。
***清单 17-16。*为水平处理设计标签
.tabTreatment1 .tabTriggerLI { -webkit-border-top-right-radius: 5px; -webkit-border-bottom-right-radius: 0; -webkit-border-bottom-left-radius: 0; -webkit-border-top-left-radius: 5px; -moz-border-radius-topright: 5px; -moz-border-radius-bottomright: 0; -moz-border-radius-bottomleft: 0; -moz-border-radius-topleft: 5px; border-top-right-radius: 5px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; border-top-left-radius: 5px; -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; background: blue; border: 1px solid #444; background: #fffbfb; background: blue; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmJmYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjYl IiBzdG9wLWNvbG9yPSIjZmZmOWY5IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMjUlIiBzdG9wLWNv bG9yPSIjZmZlYmViIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMjUlIiBzdG9wLWNvbG9yPSIjZmVl OGU4IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMzIlIiBzdG9wLWNvbG9yPSIjZmVlNGU0IiBzdG9w LW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMzMlIiBzdG9wLWNvbG9yPSIjZmVlMWUxIiBzdG9wLW9wYWNpdHk9 IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjZmVkNWQ1IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAg IDxzdG9wIG9mZnNldD0iNTElIiBzdG9wLWNvbG9yPSIjZmRjNmM3IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9m ZnNldD0iNjYlIiBzdG9wLWNvbG9yPSIjZmNiZWJmIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iOTIl IiBzdG9wLWNvbG9yPSIjZmRhYWFiIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1j b2xvcj0iI2ZkYTdhOCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIw IiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=); background: -moz-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e 4 32%, #fee1e1 33%, #fed5d5 50%, #fdc6c7 51%, #fcbebf 66%, #fdaaab 92%, #fda7a8 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fffbfb), color-
stop(6%, #fff9f9), color-stop(25%, #ffebeb), color-stop(25%, #fee8e8), color-stop(32%, #fee4e4), color-stop(33%, #fee1e1), color-stop(50%, #fed5d5), color-stop(51%, #fdc6c7), color-stop(66%, #fcbebf), color-stop(92%, #fdaaab), color-stop(100%, #fda7a8)); background: -webkit-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fdc6c7 51%, #fcbebf 66%, #fdaaab 92%, #fda7a8 100%); background: -o-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fdc6c7 51%, #fcbebf 66%, #fdaaab 92%, #fda7a8 100%); background: -ms-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fdc6c7 51%, #fcbebf 66%, #fdaaab 92%, #fda7a8 100%); background: linear-gradient(to bottom, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fdc6c7 51%, #fcbebf 66%, #fdaaab 92%, #fda7a8 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbfb', endColorstr='#fda7a8', GradientType=0); }
清单 17-17 展示了我们如何为水平处理设置活动标签的外观。正如在清单 17-16 的描述中所讨论的,我们覆盖了我们在
.tabTreatment .tabTriggerLI
规则中设置的属性(在清单 17-16 中描述的规则)。首先,我们关闭底部边框,这样包含标签的区域看起来像是加入了包含内容的区域。然后我们把背景色改成稍微亮一点的颜色。最后,我们覆盖渐变设置来提供一个稍微亮一点的渐变。***清单 17-17。*为水平处理设置活动标签样式
.tabTreatment1 .activeTab { /* * We're setting the style for the selected tab */ border-bottom: none; background: #fffbfb; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmJmYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjYl IiBzdG9wLWNvbG9yPSIjZmZmOWY5IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMjUlIiBzdG9wLWNv bG9yPSIjZmZlYmViIiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMjUlIiBzdG9wLWNvbG9yPSIjZmVl OGU4IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMzIlIiBzdG9wLWNvbG9yPSIjZmVlNGU0IiBzdG9w LW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iMzMlIiBzdG9wLWNvbG9yPSIjZmVlMWUxIiBzdG9wLW9wYWNpdHk9 IjEiLz4KICAgIDxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjZmVkNWQ1IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAg IDxzdG9wIG9mZnNldD0iNTElIiBzdG9wLWNvbG9yPSIjZmNkNGQ0IiBzdG9wLW9wYWNpdHk9IjEiLz4KICAgIDxzdG9wIG9m ZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZjZmNmYyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4K ICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQp IiAvPgo8L3N2Zz4=); background: -moz-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e 4 32%, #fee1e1 33%, #fed5d5 50%, #fcd4d4 51%, #fcfcfc 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fffbfb), color- stop(6%, #fff9f9), color-stop(25%, #ffebeb), color-stop(25%, #fee8e8), color-stop(32%, #fee4e4), color-stop(33%, #fee1e1), color-stop(50%, #fed5d5), color-stop(51%, #fcd4d4), color-stop(100%, #fcfcfc)); background: -webkit-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fe
e4e4 32%, #fee1e1 33%, #fed5d5 50%, #fcd4d4 51%, #fcfcfc 100%); background: -o-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fcd4d4 51%, #fcfcfc 100%); background: -ms-linear-gradient(top, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fcd4d4 51%, #fcfcfc 100%); background: linear-gradient(to bottom, #fffbfb 0%, #fff9f9 6%, #ffebeb 25%, #fee8e8 25%, #fee4e4 32%, #fee1e1 33%, #fed5d5 50%, #fcd4d4 51%, #fcfcfc 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbfb', endColorstr='#fcfcfc', GradientType=0); }
清单 17-18 显示了我们如何设计组成标签的锚标签的样式。我们所做的就是将颜色设置为深灰色(我们发现灰色比纯黑色更有吸引力)。
***清单 17-18。*设置水平处理的标签颜色
.tabTreatment1 .tabTriggerA { color: #222; }
清单 17-19 显示了我们如何设计一个标签的内容区域。首先,我们将边框设置为灰色。然而,因为我们不想在顶部设置边框,所以我们关闭了顶部边框。之后,我们创建圆角,设置内容区域周围的阴影,并设置当访问者改变标签时产生动画的过渡。因为我们必须让这些设置在许多不同的浏览器上工作,所以这些任务的代码有一个很大的清单。
***清单 17-19。*对内容区进行横向处理
.tabTreatment1 .tabContentUL { border: 1px solid #444; border-top: none; -webkit-border-top-right-radius: 0; -webkit-border-bottom-right-radius: 3px; -webkit-border-bottom-left-radius: 3px; -webkit-border-top-left-radius: 0; -moz-border-radius-topright: 0; -moz-border-radius-bottomright: 3px; -moz-border-radius-bottomleft: 3px; -moz-border-radius-topleft: 0; border-top-right-radius: 0; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; border-top-left-radius: 0; -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; -webkit-box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.4); -moz-box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.4); box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.4); /* * To achieve our animation for the height * of the content tab as it's changing content,
* we set the transition property below. This * tells it to transition on all available * transition properties when there's a change. */ -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -ms-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; }
我们将从清单 17-20 的开始,对接下来的几条规则进行分组,因为它们都适用于 Internet Explorer。IE6 和 IE7 不让我们用
display: table-cell
和display: table properties
。即使是理解display:table types
的 IE9,也有一个复杂渐变和所有事物边界的问题。因此,我们必须使用另一种方法:浮动列表项。我们还将最后一个列表项上的overflow
属性的值设置为hidden.
,这种技术使得选项卡占据了选项卡控件的整个宽度。***清单 17-20。*用于横向处理的 Internet Explorer 样式
/* * Because even IE9 still doesn't always play well, we need * to take a different approach by not using the display:table and * display:table-cell properties. Instead, we go with a more * traditional approach: floating the LIs. We're also doing a bit * of trickery by setting the last LI to overflow:hidden and taking * away the float. This trick makes the tab take up the remaining * space so that our tabs will take up the full width of the tab * control. */ .ie .tabTreatment1 .tabTriggerLI { display: block; float: left; margin: 0; position: relative; } .ie .tabTreatment1 .tabTriggerUL { display: block; overflow: hidden; } .ie .tabTreatment1 .tabLast { float: none; overflow: hidden; } .ie .tabTreatment1 .tabTriggerA { position: relative; top: 50%; display: block; }
清单 17-21 也捆绑了一些规则(在本例中是两个)。如果我们让 IE9 的
filter
属性保持活动状态,我们将得到一个简单的两级渐变。SVG 让我们有了多级渐变。因此,我们必须删除过滤器在 IE9 中获得更好的梯度。我们需要两个规则,这样我们可以为所有选项卡和活动选项卡覆盖filter
属性。***清单 17-21。*将 IE9 水平处理的滤镜设置为无
/* * We need to disable the filter property * for IE9 so we can use our SVG alternative. */ .ie9 .tabTreatment1 .tabTriggerLI { filter: none; } .ie9 .tabTreatment1 .activeTab { filter: none; }
垂直处理的样式
现在我们来看看应用于选项卡控件的垂直处理的样式。数值
tabTreatment2
表示垂直处理。我们首先设置一大组适用于整个处理的属性。清单 17-22 展示了我们如何设置一些适用于整个处理的属性。我们从设置宽度开始。然后我们设置
overflow
属性的值来清除浮动。然后我们设置填充和背景颜色。接下来,我们设置渐变和创建圆角。和往常一样,对于这些属性,我们必须为每个属性设置许多行,以便在尽可能多的浏览器上获得我们想要的外观。***清单 17-22。*设置整个垂直处理的属性
.tabTreatment2 { width: 605px; overflow: hidden; padding: 10px; background: #45484d; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzQ1NDg0ZCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEw MCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3Qg eD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9z dmc+); background: -moz-linear-gradient(top, #45484d 0%, #000000 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #45484d), color- stop(100%, #000000)); background: -webkit-linear-gradient(top, #45484d 0%, #000000 100%); background: -o-linear-gradient(top, #45484d 0%, #000000 100%); background: -ms-linear-gradient(top, #45484d 0%, #000000 100%); background: linear-gradient(to bottom, #45484d 0%, #000000 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#45484d', endColorstr='#00000 0', GradientType=0);
border: 1px solid; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; }
清单 17-23 展示了我们如何将包含标签的列表转换成一个表格进行垂直处理。因为我们将宽度设置为 100 %,所以选项卡控件现在填充了它的可用空间。与适用于所有处理的规则不同(参见清单 17-9 ,该规则将
float
属性的值设置为left
,宽度设置为 150 像素,右边距设置为 10 像素。***清单 17-23。*将列表变成表格进行垂直处理
.tabTreatment2 .tabTriggerUL { display: block; width: 150px; float: left; height: 100%; margin-right: 10px; }
清单 17-24 展示了我们如何在水平处理的选项卡控件中设计实际的选项卡(每个选项卡标签周围的结构)。我们使用这个规则来规定非活动选项卡的外观。当一个选项卡被激活时,我们会覆盖其中的一些值。在这里,我们指定填充、对齐、边距和阴影。我们还确保选项卡显示为一个块,并且相对定位。然后我们设置渐变和背景色。
***清单 17-24。*为垂直处理设计标签
.tabTreatment2 .tabTriggerLI { padding: 10px; text-align: center; position: relative; display: block; margin: 4px 0; -webkit-box-shadow: 0 0 3px rgba(255, 255, 255, 0.7); -moz-box-shadow: 0 0 3px rgba(255, 255, 255, 0.7); box-shadow: 0 0 3px rgba(255, 255, 255, 0.7); background: #7d7e7d; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzdkN2U3ZCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEw MCUiIHN0b3AtY29sb3I9IiMwZTBlMGUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3Qg eD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9z dmc+); background: -moz-linear-gradient(top, #7d7e7d 0%, #0e0e0e 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #7d7e7d), color-
stop(100%, #0e0e0e)); background: -webkit-linear-gradient(top, #7d7e7d 0%, #0e0e0e 100%); background: -o-linear-gradient(top, #7d7e7d 0%, #0e0e0e 100%); background: -ms-linear-gradient(top, #7d7e7d 0%, #0e0e0e 100%); background: linear-gradient(to bottom, #7d7e7d 0%, #0e0e0e 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#7d7e7d', endColorstr='#0e0e0e', GradientType=0); }
清单 17-25 显示了我们如何为标签控件的垂直处理设计标签样式。首先,我们移除文本装饰,因为我们不希望文本加下划线。然后,我们确保文本是一个块,并设置颜色为白色。然后,我们确保文本相对定位,并将顶部距离设置为 50%。
***清单 17-25。*为垂直处理设计标签样式
.tabTreatment2 .tabTriggerA { text-decoration: none; display: block; color: white; position: relative; top: 50%; }
清单 17-26 展示了我们如何为垂直处理设计内容区域的样式。首先,我们确保内容显示为一个块。然后,我们将边框设置为中灰色。接下来,我们将定位设置为相对的。然后我们防止内容溢出内容区,因为那样看起来很糟糕。最后,我们设置当访问者改变标签时提供动画的过渡效果。
***清单 17-26。*为垂直处理的内容区域设置样式
.tabTreatment2 .tabContentUL { display: block; border: 1px solid #999; position: relative; overflow: hidden; /* * To achieve our animation for the height * of the content tab as it's changing content, * we set the transition property below. This * tells it to transition on all available * transition properties when there's a change. */ -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -ms-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; }
清单 17-27 展示了我们如何为垂直处理设计标签的内容区域。我们将各种
opacity
属性的值设置为 0,将visibility
属性的值设置为 0,并将z-index
设置为 1。这些设置使内容区域不可见。我们将为活动选项卡覆盖这些值。我们还设置了阴影、背景渐变和背景颜色来控制内容区域的外观。在这个过程中,我们设置了当访问者改变标签时提供动画的转换值。因为我们必须让这些设置在许多不同的浏览器上工作,所以这些任务的代码有一个很大的清单。***清单 17-27。*为垂直处理的内容区域设置样式
.tabTreatment2 .tabContentLI { /* * We're starting with an opacity of 0 as the * default state, and when the tab content is triggered * by the tab trigger, it will animate into 100% opacity. * We're also setting the visibility to hidden and changing * that to a visibility of visible on trigger selection * to accommodate browsers that don't handle opacity. */ -webkit-opacity: 0; -moz-opacity: 0; opacity: 0; padding: 20px; -webkit-box-shadow: inset 2px 2px 2px rgba(0, 0, 0, 0.6); -moz-box-shadow: inset 2px 2px 2px rgba(0, 0, 0, 0.6); box-shadow: inset 2px 2px 2px rgba(0, 0, 0, 0.6); position: absolute; visibility: hidden; top: 0; left: 0; -webkit-transition: all 0.3s ease-out; -moz-transition: all 0.3s ease-out; -ms-transition: all 0.3s ease-out; -o-transition: all 0.3s ease-out; transition: all 0.3s ease-out; z-index: 1; background: #ffffff; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjQ3 JSIgc3RvcC1jb2xvcj0iI2Y2ZjZmNiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3At Y29sb3I9IiNlZGVkZWQiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0i MCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #ededed 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(47%, #f6f6f6), color- stop(100%, #ededed)); background: -webkit-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #ededed 100%); background: -o-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #ededed 100%); background: -ms-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #ededed 100%); background: linear-gradient(to bottom, #ffffff 0%, #f6f6f6 47%, #ededed 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed', GradientType=0);
}
清单 17-28 展示了我们如何为垂直处理制作一个可见的标签。为此,我们将属性
opacity
的值重写为 1,属性visibility
的值重写为visible
,属性z-index
的值重写为 2。该设置集合使选项卡可见。***清单 17-28。*使垂直治疗的标签可见
.tabTreatment2 .showTab { -webkit-opacity: 1; -moz-opacity: 1; opacity: 1; z-index: 2; visibility: visible; }
清单 17-29 展示了我们如何为垂直处理设置活动标签的样式。在这种情况下,我们只需要设置背景颜色和背景渐变。和以往一样,当我们需要渐变时,需要相当多的代码,不仅因为我们使用 SVG,还因为我们必须为所有可能的浏览器设置它。
***清单 17-29。*设计垂直处理的活动标签
.tabTreatment2 .activeTab { background: #444444; background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRw Oi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJl c2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFk aWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Ag b2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzQ0NDQ0NCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEw MCUiIHN0b3AtY29sb3I9IiM5ZTllOWUiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3Qg eD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9z dmc+); background: -moz-linear-gradient(top, #444444 0%, #9e9e9e 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #444444), color-stop(100%, #9e9e9e)); background: -webkit-linear-gradient(top, #444444 0%, #9e9e9e 100%); background: -o-linear-gradient(top, #444444 0%, #9e9e9e 100%); background: -ms-linear-gradient(top, #444444 0%, #9e9e9e 100%); background: linear-gradient(to bottom, #444444 0%, #9e9e9e 100%); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#444444', endColorstr='#9e9e9e', GradientType=0); }
清单 17-30 显示了我们如何为垂直处理关闭 IE9 的滤波器。因为 IE 9 会呈现一个简单得多的两级渐变,而我们想要更好的多级 SVG 渐变,所以我们覆盖了垂直处理的属性
filter
的值,这样 IE9 的值就是none
。***清单 17-30。*关闭 IE9 的滤镜。
.ie9 .tabTreatment2 .activeTab { filter: none; }
总结
本章展示了如何开发和使用选项卡控件。和我们所有的控件一样,我们使用一个单独的 HTML 块,由作为控件核心的函数从数据中生成。从那块积木上,我们制作了两种不同的处理方式,一种是水平的浅色处理,一种是垂直的深色处理。我们还采用了我们的标准范例,尽可能使用 CSS3,但是在 JavaScript 中为不能使用 CSS3 的浏览器提供了一个后备。因此,我们接受渐进式设计的原则,给每个访问者提供访问者浏览器所能提供的最佳体验。如前所述,我们不试图为每个访问者提供相同的体验。我们认为这是一个错误的目标,因为不同的浏览器不可能有真正相同的体验。相反,我们的目标是在任何浏览器上都有良好的体验。
对于这个控件,我们还创建了一个 jQuery 插件,让我们为特定的选项卡创建一个地址。这样,返回或刷新页面的访问者会得到他们刚才所在的标签页。在一组选项卡上设置唯一标识符的能力还允许一个页面拥有多组选项卡。该地址(由
id
属性启用)还允许我们创建到特定选项卡的链接。为了增加一点视觉趣味,我们还制作了从一个选项卡到另一个选项卡的动画。所有这些工作的最终结果是一个可重用的选项卡控件,它看起来很好,并且比我们在各种网站上找到的大多数选项卡排列具有更好的功能。