为什么不使用表格布局
简单来说,使用表格布局而不使用 CSS 布局技巧 是很糟糕的。主要的理由有以下几个:
- 表格布局减少了视觉受损的用户的无障碍: 盲人所使用的屏幕阅读器会解析存在于 HTML 页面上的标签,然后为用户读出其中的内容。因为对于布局来说,表格不是一个正确的工具,使用的标记比使用 CSS 布局技术更复杂,所以屏幕阅读器的输出会让他们的用户感到困惑。
- 表格会产生很多标签: 正如刚才提到的,表格布局通常会比正确的布局技术涉及更复杂的标签结构,这会导致代码变得更难于编写、维护、调试。
- 表格不能自动响应: 当你使用正确的布局容器(比如
<header>
、<section>
、<article>
或是<div>
)时,它们的默认宽度是父元素的 100%;而表格的默认大小是根据其内容而定的。因此,需要采取额外的措施来获取表格布局样式,以便有效地在各种设备上工作。
使用 应用样式
xml
<table>
<colgroup>
<col />
<col style="background-color: yellow" />
</colgroup>
<tr>
<th>Data 1</th>
<th>Data 2</th>
</tr>
<tr>
<td>Calcutta</td>
<td>Orange</td>
</tr>
<tr>
<td>Robots</td>
<td>Jazz</td>
</tr>
</table>
我们使用了两个 <col>
来定义"列的样式",每一个 <col>
都会指定每列的样式,对于第一列,我们没有采取任何样式,但是我们仍然需要添加一个空的 <col>
元素,如果不这样做,那么我们的样式就会应用到第一列上,这和我们预想的不一样。
如果你想把这种样式信息应用到每一列,我们可以只使用一个 <col>
元素,不过需要包含 span 属性,像这样:
HTMLCopy to Clipboard
ini
<colgroup<col style="background-color: yellow" span="2" /></colgroup
就像 colspan
和 rowspan
一样,span
需要一个无单位的数字值,用来指定让这个样式应用到表格中多少列。
HTML 表格高级特性和无障碍
developer.mozilla.org/zh-CN/docs/...
CSS 用来干什么?
一份文档是由标记语言组织起来的文本文件------HTML 是最常见的标记语言,但你可能也听说过其他可标记语言,如 SVG 或 XML。
展示一份文档给用户实际上是将文档变成用户可用的文件。浏览器:如 Firefox、Chrome 或 Edge,都可以将文档在电脑屏幕、投影仪或打印机等设备上进行可视化。
备注: 浏览器有时候也被称为 user agent,大致可以当这个程序是一个存在于计算机系统中的人。当我们讨论 CSS 时,浏览器是 User agent 的主要形式,然而它并不是唯一的一个。还有其他可用的 user agents --- 像是那些可以把 HTML 和 CSS 文档转换为可以打印的 PDF 文档的软件。
CSS 可以用于给文档添加样式------比如改变标题和链接的颜色及大小。它也可用于创建布局------比如将一个单列文本变成包含主要内容区域和存放相关信息的侧边栏区域的布局。它甚至还可以用来做一些特效,比如动画。查看本段内容中所给出的特定案例。
作为一个 CSS 新手,你会发现阅读 CSS 规范 中的内容非常吃力------它旨在为工程师在用户代理 (user agents) 中实现对 CSS 各种特性的支持,而不是作为一本为 Web 开发者理解 CSS 内容的教程。即使是有经验的开发者,也更倾向于使用 MDN 文档或者其他教程。但是,知晓它的存在,理解 CSS、规范 和 浏览器支持(见下文)之间的关系是很有价值的。
将 CSS 属性设置为特定值是定义文档布局和样式的主要方式。CSS 引擎会计算哪些声明适用于页面的每一个元素。
如果属性未知或某个值对给定属性无效,则声明被视为无效,并被浏览器的 CSS 引擎完全忽略。
使用 CSS 简写属性的一个不太明显的方面是省略的值如何重置。一个没有在 CSS 简写属性中指定的值会恢复到它的初始值。这意味着 CSS 简写属性中的省略可以覆盖之前设置的值。
@规则
CSS 的 @rules
(读作"at-rules")是一些特殊的规则,提供了关于 CSS 应该执行什么或如何表现的指令。有些@规则很简单,只有一个关键词和一个值。例如,@import
将一个样式表导入另一个 CSS 样式表:
scss
@import "styles2.css";
你可能遇到的一个常见的@规则是 @media
,它被用来创建媒体查询。媒体查询使用条件逻辑来应用 CSS 样式。
在下面的例子中,样式表为 <body>
元素定义了一个默认的粉红色背景。然而,如果浏览器的视口宽于 30em,接下来的媒体查询则定义了蓝色背景。
css
body {
background-color: pink;
}
@media (min-width: 30em) {
body {
background-color: blue;
}
}
CSS 究竟是怎么工作的?
当浏览器展示一个文件的时候,它必须兼顾文件的内容和文件的样式信息,下面我们会了解到它处理文件的标准的流程。需要知道的是,下面的步骤是浏览加载网页的简化版本,而且不同的浏览器在处理文件的时候会有不同的方式,但是下面的步骤基本都会出现。
- 浏览器载入 HTML 文件(比如从网络上获取)。
- 将 HTML 文件转化成一个 DOM(Document Object Model),DOM 是文件在计算机内存中的表现形式,下一节将更加详细的解释 DOM。
- 接下来,浏览器会拉取该 HTML 相关的大部分资源,比如嵌入到页面的图片、视频和 CSS 样式。JavaScript 则会稍后进行处理,简单起见,同时此节主讲 CSS,所以这里对如何加载 JavaScript 不会展开叙述。
- 浏览器拉取到 CSS 之后会进行解析,根据选择器的不同类型(比如 element、class、id 等等)把他们分到不同的"桶"中。浏览器基于它找到的不同的选择器,将不同的规则(基于选择器的规则,如元素选择器、类选择器、id 选择器等)应用在对应的 DOM 的节点中,并添加节点依赖的样式(这个中间步骤称为渲染树)。
- 上述的规则应用于渲染树之后,渲染树会依照应该出现的结构进行布局。
- 网页展示在屏幕上(这一步被称为着色)。
结合下面的图示更形象:
关于 DOM
一个 DOM 有一个树形结构,标记语言中的每一个元素、属性以及每一段文字都对应着结构树中的一个节点(Node/DOM 或 DOM node)。节点由节点本身和其他 DOM 节点的关系定义,有些节点有父节点,有些节点有兄弟节点(同级节点)。
为什么指定多个width来解决兼容性问题
当浏览器遇到无法解析的 CSS 代码会发生什么
在之前的文章中我们提到了浏览器并不会同时实现所有的新 CSS,此外很多人也不会使用最新版本的浏览器。鉴于 CSS 一直不断的开发,因此领先于浏览器可以识别的范围,那么你也许会好奇当浏览器遇到无法解析的 CSS 选择器或声明的时候会发生什么呢?
答案就是浏览器什么也不会做,继续解析下一个 CSS 样式!
如果一个浏览器在解析你所书写的 CSS 规则的过程中遇到了无法理解的属性或者值,它会忽略这些并继续解析下面的 CSS 声明。在你书写了错误的 CSS 代码(或者误拼写),又或者当浏览器遇到对于它来说很新的还没有支持的 CSS 代码的时候上述的情况同样会发生(直接忽略)。
相似的,当浏览器遇到无法解析的选择器的时候,他会直接忽略整个选择器规则,然后解析下一个 CSS 选择器。
这样做好处多多,代表着你使用最新的 CSS 优化的过程中浏览器遇到无法解析的规则也不会报错。当你为一个元素指定多个 CSS 样式的时候,浏览器会加载样式表中的最后的 CSS 代码进行渲染(样式表,优先级等请读者自行了解),也正因为如此,你可以为同一个元素指定多个 CSS 样式来解决有些浏览器不兼容新特性的问题(比如指定两个 width
)。
这一特点在你想使用一个很新的 CSS 特性但是不是所有浏览器都支持的时候(浏览器兼容)非常有用,举例来说,一些老的浏览器不接收calc()
(calculate 的缩写,CSS3 新增,为元素指定动态宽度、长度等,注意此处的动态是计算之后得一个值) 作为一个值。我可能使用它结合像素为一个元素设置了动态宽度(如下),老式的浏览器由于无法解析忽略这一行;新式的浏览器则会把这一行解析成像素值,并且覆盖第一行指定的宽度。
CSS选择器相关
当你使用选择器列表时,如果任何一个选择器无效 (存在语法错误),那么整条规则都会被忽略。
使用全局选择器,让选择器更易读
全局选择器的一种用法是让选择器更易读,更明显地表明它们的作用。例如,如果我想选中任何 <article>
元素的第一子元素 ,不论它是什么元素,都给它加粗,我可以将:first-child
选择器(我们将会在伪类和伪元素课中进一步了解)用作<article>
元素选择器的一个后代选择器:
ruby
article :first-child {}
但是这会和article:first-child
混淆,而后者选择了作为其他元素的第一子元素的<article>
元素。
为了避免这种混淆,我们可以向:first-child
选择器加入全局选择器,这样选择器所做的事情很容易就能看懂。选择器正选中<article>
元素的任何第一子元素:
ruby
article *:first-child {}
为什么不是使用 ID选择器
正如我们在和特定性相关的课里面学到的那样,ID 所指特定,会优先于大多数其他选择器。所以很难处理它们。大多数情况下,给一个元素加个类,而不是使用 ID,会更好。不过要是 ID 是唯一一种指定这个元素的方式的话------也许是因为你没法访问标记标记因此不能编辑------这种方式可行。
存否和值选择器
这些选择器允许基于一个元素自身是否存在(例如href
)或者基于各式不同的按属性值的匹配,来选取元素。
表格 还在加载中,请等待加载完成后再尝试复制
下面的示例中,你可以看到这些选择器是怎样使用的。
-
使用
li[class]
,我们就能匹配任何有 class 属性的选择器。这匹配了除了第一项以外的所有项。 -
li[class="a"]
匹配带有一个a
类的选择器,不过不会选中一部分值为a
而另一部分是另一个用空格隔开的值的类,它选中了第二项。 -
li[class~="a"]
会匹配一个a
类,不过也可以匹配一列用空格分开、包含a
类的值,它选中了第二和第三项。
子字符串匹配选择器
这些选择器让更高级的属性的值的子字符串的匹配变得可行。例如,如果你有box-warning
和box-error
类,想把开头为"box-"字符串的每个物件都匹配上的话,你可以用[class^="box-"]
来把它们两个都选中。
表格 还在加载中,请等待加载完成后再尝试复制
下个示例展示了这些选择器的用法:
li[class^="a"]
匹配了任何值开头为a
的属性,于是匹配了前两项。li[class$="a"]
匹配了任何值结尾为a
的属性,于是匹配了第一和第三项。li[class*="a"]
匹配了任何值的字符串中出现了a
的属性,于是匹配了所有项。
大小写敏感 切换
如果你想在大小写不敏感的情况下,匹配属性值的话,你可以在闭合括号之前,使用i
值。这个标记告诉浏览器,要以大小写不敏感的方式匹配 ASCII 字符。没有了这个标记的话,值会按照文档语言对大小写的处理方式,进行匹配------HTML 中是大小写敏感的。
下面的示例中,第一个选择器将会匹配一个开头为a
的值,这样它只匹配了第一项,因为另外两项开头是大写的 A。第二个选择器使用了大小写不敏感的标记,于是匹配了所有项。
此外还有一个更加新的s
值,它会强制在上下文的匹配正常为大小写不敏感的时候,强行要求匹配时大小写敏感。不过,在浏览器中它不太受支持,而且在上下文为 HTML 时也没啥用。
伪元素和伪类
假如我们不加类,我们可以使用:first-child
伪类选择器------这将一直选中文章中的第一个子元素,我们将不再需要编辑 HTML(编辑 HTML 并不总是可行,也许是因为它是由一个 CMS 生成的)
一些早期的伪元素曾使用单冒号的语法,所以你可能会在代码或者示例中看到。现代的浏览器为了保持后向兼容,支持早期的带有单双冒号语法的伪元素。
例如,如果你想选中一段的第一行,你可以把它用一个<span>
元素包起来,然后使用元素选择器;不过,如果包起来的单词/字符数目长于或者短于父元素的宽度,这样做会失败。由于我们一般不会知道一行能放下多少单词/字符------因为屏幕宽度或者字体大小改变的时候这也会变------通过改变 HTML 的方式来可预测地这么做是不可能的。
::first-line
伪元素选择器会值得信赖地做到这件事------即使单词/字符的数目改变,它也只会选中第一行。
有一组特别的伪元素,它们和content
属性一同使用,使用 CSS 将内容插入到你的文档中中。::before
,::after
从 CSS 插入文本字符串,我们并不会在 Web 浏览器上经常这么做,因为对于一些屏幕阅读器来说,文本是不可见的,而且对于未来别人的查找和编辑也不是很方便。
这些伪元素的更推荐的用法是插入一个图标,例如下面的示例加入的一个小箭头,作为一个视觉性的提示,而且我们并不希望屏幕阅读器读出它。
::before
和::after
伪元素与content
属性的共同使用,在 CSS 中被叫做"生成内容",而且你会见到这种技术被用于完成各种任务。CSS Arrow Please网站就是一个著名的示例,它帮你用 CSS 生成一个箭头。在你创建你的箭头的时候看下 CSS,你将会看到实际使用的::before
和::after
伪元素。无论什么时候你看到了这些选择器,都要看下content
属性,以了解文档中添加了什么
汇总
表格 还在加载中,请等待加载完成后再尝试复制
表格 还在加载中,请等待加载完成后再尝试复制
关系选择器
层叠、优先级与继承
developer.mozilla.org/zh-CN/docs/...
尽管每个 CSS 属性页都列出了属性是否被继承,但我们通常可以通过常识来判断哪些属性属于默认继承。
控制继承
CSS 为控制继承提供了五个特殊的通用属性值。每个 CSS 属性都接收这些值。
inherit
设置该属性会使子元素属性和父元素相同。实际上,就是"开启继承"。
initial
将应用于选定元素的属性值设置为该属性的初始值。
revert (en-US)
将应用于选定元素的属性值重置为浏览器的默认样式,而不是应用于该属性的默认值。在许多情况下,此值的作用类似于 unset
。
revert-layer (en-US)
将应用于选定元素的属性值重置为在上一个层叠层中建立的值。
unset
将属性重置为自然值,也就是如果属性是自然继承那么就是 inherit
,否则和 initial
一样
我们可以查看一个链接列表来探索这些值是如何运作的。在下面的实例中,你可以通过修改 CSS 来查看它们的功能,写代码是掌握 HTML 和 CSS 最好的办法。
示例:
- 第二个列表项应用了类
my-class-1
。它设置了内部元素来继承属性。如果你删除这个类,它会如何改变链接的颜色? - 你知道为什么第三个和第四个链接会是这样的颜色?第三个链接设置了
initial
,这意味着它使用了属性的初始值(在本例中为黑色),而不是链接的浏览器默认样式的蓝色。第四个设置了unset
,这意味着链接文本会使用其父元素的颜色------绿色。 - 如果你为
<a>
元素定义新的颜色(例如:a { color: red; }
),哪些链接会改变颜色?
重设所有属性值
CSS 的简写属性 all
可以用于同时将这些继承值中的一个应用于(几乎)所有属性。它的值可以是其中任意一个(inherit
、initial
、unset
或 revert
)。这是一种撤销对样式所做更改的简便方法,以便回到之前已知的起点。
下面的示例中有两个块级引用元素。第一个用元素本身的样式,第二个设置 all
为 unset
优先级计算规则
现在让我们来看看浏览器如何计算优先级。我们已经知道一个元素选择器比类选择器的优先级更低,会被其覆盖。本质上,不同类型的选择器有不同的分数值,把这些分数相加就得到特定选择器的权重,然后就可以进行匹配。
一个选择器的优先级可以说是由三个不同的值(或分量)相加,可以认为是百(ID)十(类)个(元素)------三位数的三个位数:
- ID:选择器中包含 ID 选择器则百位得一分。
- 类:选择器中包含类选择器、属性选择器或者伪类则十位得一分。
- 元素:选择器中包含元素、伪元素选择器则个位得一分。
备注: 通用选择器(*
)、组合符(+
、、~
、' ')和调整优先级的选择器(:where()
)不会影响优先级。
否定(:not()
)和任意匹配(:is()
)伪类本身对优先级没有影响,但它们的参数则会带来影响。参数中,对优先级算法有贡献的参数的优先级的最大值将作为该伪类选择器的优先级。
表格 还在加载中,请等待加载完成后再尝试复制
内联样式,即 style
属性内的样式声明,优先于所有普通的样式,无论其优先级如何。这样的声明没有选择器,但它们的优先级可以理解为 1-0-0-0;即无论选择器中有多少个 ID,它总是比其他任何优先级的权重都要高。
有一个特殊的 CSS 可以用来覆盖所有上面所有优先级计算,不过需要很小心的使用------!important
。用于修改特定属性的值,能够覆盖普通规则的层叠。
备注: 了解 !important
是为了在阅读别人代码的时候知道有什么作用。但是,强烈建议除了非常情况不要使用它。!important
改变了层叠的常规工作方式,它会使调试 CSS 问题非常困难,特别是在大型样式表中。
覆盖 !important
唯一的办法就是另一个 !important
具有相同优先级而且顺序靠后,或者更高优先级。
覆盖声明的顺序
相互冲突的声明将按以下顺序应用,后一种声明将覆盖前一种声明:
- 用户代理样式表中的声明(例如,浏览器的默认样式,在没有设置其他样式时使用)。
- 用户样式表中的常规声明(由用户设置的自定义样式)。
- 作者样式表中的常规声明(这些是我们 web 开发人员设置的样式)。
- 作者样式表中的
!important
声明 - 用户样式表中的
!important
声明 - 用户代理样式表中的
!important
声明
记为 !important
的样式的优先级顺序是颠倒的。web 开发人员的样式表覆盖用户的样式表是有意义的,因此设计可以按预期进行,但是有时用户有充足的理由覆盖 web 开发人员的样式,正如上面提到的------这可以通过在他们的规则中使用 !important
来实现。
级联层的顺序
尽管级联层属于高级的主题,你可能不会立刻使用此特性,但了解层是如何级联的非常重要。
在级联层中声明 CSS 是,优先级的顺序由声明层的顺序来决定。在任何层之外声明的 CSS 样式会被按声明的顺序组合在一起,形成一个未命名的层,它会被当作最后声明的层。对于存在冲突的常规(没有 !important
声明)样式,后面的层比先前定义的层的优先级高。但对于带有 !important
标记的样式,其顺序相反------先前的层中的 important 样式比后面的层以及为在层中声明的 important 样式优先级要高。但内联样式比所有作者定义的样式的优先级都要高,不受级联层规则的影响。
当你在不同的层中有多个样式块,且其中提供了对于某一元素的单一属性的相互冲突的值时,声明该冲突样式的层的顺序将决定其优先级。而不是高优先级的层直接覆盖低优先级的层中的所有样式。需要注意的是单独的一个层中的样式的优先级仍旧会起作用。
让我们讨论一下上例以了解发生了什么。示例中,声明了两个级联层,按 firstLayer
和 secondLayer
的顺序声明。即使 secondLayer
的优先级更高,但其中声明的样式没有被使用。为什么?因为不分层的常规样式具有更高的优先级,覆盖了层中的常规样式,不论层的优先级如何,而具有 important 的样式,则是先前声明的层会覆盖后声明的层,而不是层的优先级。
如果你修改示例中 CSS 的第一行为 @layer secondLayer, firstLayer;
,就会修改层声明的顺序,所有 firstLayer
中的 important 样式会被 secondLayer
中的冲突值覆盖。