行吟阁 2020-07-05
HTML 并不简单,它是典型的“入门容易,精通困难”的一部分知识。深刻理解 HTML 是成为优秀的前端工程师重要的一步。
语义类标签是工作中经常会用到的一类标签,它们的特点是视觉表现上互相都差不多,主要的区别在于它们表示了不同的语义,比如大家会经常见到的 section、nav、p,这些都是语义类的标签。
语义是我们说话表达的意思,多数的语义实际上都是由文字来承载的。语义类标签则是纯文字的补充,比如标题、自然段、章节、列表,这些内容都是纯文字无法表达的,我们需要依靠语义标签代为表达。
在讲语义之前,我们来说说为什么要用语义。
现在很多的前端工程师写起代码来,多数都不用复杂的语义标签, 只靠 div 和 span 就能走天下了。
这样做行不行呢?毫无疑问答案是行。那这样做好不好呢?按照正确的套路,我应该说不好,但是在很多情况下,答案其实是好。
这是因为在现代互联网产品里,HTML 用于描述“软件界面”多过于“富文本”,而软件界面里的东西,实际上几乎是没有语义的。比如说,我们做了一个购物车功能,我们一定要给每个购物车里的商品套上 ul 吗?比如说,加入购物车这个按钮,我们一定要用 Button 吗?
实际上我觉得没必要,因为这个场景里面,跟文本中的列表,以及表单中的 Button,其实已经相差很远了,所以,我支持在任何“软件界面”的场景中,直接使用 div 和 span。
不过,在很多工作场景里,语义类标签也有它们自己无可替代的优点。正确地使用语义标签可以带来很多好处。
不过,不恰当地使用语义标签,反而会造成负面作用。这里我们举一个常见的误区作为例子。我们都知道 ul 是无序列表,ol 是有序列表,所以很多接触过语义这个概念,半懂不懂的前端工程师,特别喜欢给所有并列关系的元素都套上 ul。
实际上, ul 是长成下面的这种样子的 (以下来自 HTML 标准)。
I have lived in the following countries:
ul 多数出现正在行文中间,它的上文多数在提示:要列举某些项。但是,如果所有并列关系都用 ul,会造成大量冗余标签。
错误地使用语义标签,会给机器阅读造成混淆、增加嵌套,给 CSS 编写加重负担。
所以,对于语义标签,态度是:“用对”比“不用”好,“不用”比“用错”好。当然了,我觉得有理想的前端工程师还是应该去追求“用对”它们。
与 JavaScript 这样严格的编程语言相比,HTML 中语义标签的使用更接近我们平常说话用的自然语言。我们说话并没有唯一的标准措辞,语义标签的使用也是一样。下面,我挑选了几种(我认为)比较重要的语义标签使用场景,来为你介绍一下。
语义标签的使用的第一个场景,也是最自然的使用场景,就是:作为自然语言和纯文本的补充,用来表达一定的结构或者消除歧义。
在日语中,有一个语法现象叫做:ルビ,它的读音是 ruby(著名的 ruby 语言就是据此命名的),它中文的意思大约类似于注音或者意思的注解,它的形式可以看下图:
在 HTML5 中,就引入了这个表示 ruby 的标签,它由 ruby、rt、rp 三个标签来实现。
所以说,这些情况里存在的语义,其实原本就存在了,只是我们用纯文字是没法表达的,HTML 作为一种“超文本”语言,支持这些文字表达就是必要的了。
当没有上下文时,如何消除歧义呢?这就要用到我们的 em 标签了。em 表示重音:
今天我吃了一个<em>苹果</em>。 今天我吃了<em>一个</em>苹果。
通过 em 标签,我们可以消除这样的歧义。
一些文章常常会拿 em 和 strong 做对比,实际上,我们只要理解了 em 的真正意思,它和 strong 可谓天差地别,并没有任何混淆的可能。
介绍完自然语言的语义场景后,我想介绍的另一个语义重要使用场景,就是文章的结构。中国古代小说就形成了“章 - 回”的概念,西方的戏剧也有幕的区分,所以人类的自然语言作品也是如出一辙。
HTML 也应该支持这样的需求。HTML 语义标签中,有不少是用于支持这样的结构的标签。
语义化的 HTML 能够支持自动生成目录结构,HTML 标准中还专门规定了生成目录结构的算法,即使我们并不打算深入实践语义,也应该尽量在大的层面上保证这些元素的语义化使用。
首先我们需要形成一个概念,一篇文档会有一个树形的目录结构,它由各个级别的标题组成。这个树形结构可能不会跟 HTML 元素的嵌套关系一致。
例如: <h1>HTML语义</h1> <p>balah balah balah balah</p> <h2>弱语义</h2> <p>balah balah</p> <h2>结构性元素</h2> <p>balah balah</p> ......
这段 HTML 几乎是平铺的元素,但是它的标题结构是:
h1-h6 是最基本的标题,它们表示了文章中不同层级的标题。有些时候,我们会有副标题,为了避免副标题产生额外的一个层级,我们使用 hgroup 标签。
我们来看下有 / 无 hgroup 的对比:
<h1>JavaScript对象</h1> <h2>我们需要模拟类吗?</h2> <p>balah balah</p> ......
此段生成以下标题结构:
<hgroup> <h1>JavaScript对象</h1> <h2>我们需要模拟类吗?</h2> </hgroup> <p>balah balah</p> ......
这一段生成以下标题结构:
通过两个效果的对比就可以知道,在 hgroup 中的 h1-h6 被视为同一标题的不同组成部分。
从 HTML 5 开始,我们有了 section 标签,这个标签可不仅仅是一个“有语义的 div”,它会改变 h1-h6 的语义。section 的嵌套会使得其中的 h1-h6 下降一级,因此,在 HTML5 以后,我们只需要 section 和 h1 就足以形成文档的树形结构:
<section> <h1>HTML语义</h1> <p>balah balah balah balah</p> <section> <h1>弱语义</h1> <p>balah balah</p> </section> <section> <h1>结构性元素</h1> <p>balah balah</p> </section> ...... </section>
这段代码同样会形成前面例子的标题结构:
我们想介绍的最后一个场景是,随着越来越多的浏览器推出“阅读模式”,以及各种非浏览器终端的出现,语义化的 HTML 适合机器阅读的特性变得越来越重要。
应用了语义化结构的页面,可以明确地提示出页面信息的主次关系,它能让浏览器很好地支持“阅读视图功能”,还可以让搜索引擎的命中率提升,同时,它也对视障用户的读屏软件更友好。
我们正确使用整体结构类的语义标签,可以让页面对机器更友好。比如,这里一个典型的 body 类似这样:
<body> <header> <nav> …… </nav> </header> <aside> <nav> …… </nav> </aside> <section>……</section> <section>……</section> <section>……</section> <footer> <address>……</address> </footer> </body>
在 body 下面,有一个 header,header 里面是一个 nav,跟 header 同级的有一个 aside,aside 里面也有一个 nav。接下来是文章的整体,也就是一个一个的 section。section 里面可能还有嵌套,但是我们就不管了,最后是一个 footer,这个 footer 里面可能有 address 这样的内容。
除此之外,还有 article,article 是一种特别的结构,它表示具有一定独立性质的文章。所以,article 和 body 具有相似的结构,同时,一个 HTML 页面中,可能有多个 article 存在。
一个典型的场景是多篇新闻展示在同一个新闻专题页面中,这种类似报纸的多文章结构适合用 article 来组织。
<body> <header>……</header> <article> <header>……</header> <section>……</section> <section>……</section> <section>……</section> <footer>……</footer> </article> <article> …… </article> <article> …… </article> <footer> <address></address> </footer> </body>
body 里面有自己的 header 和 footer,然后里面是竖篇的 article,每一个 article 里面都有自己的 header、section、footer。这是一个典型的多文章结构。
在这个结构里,我们看到了一些新标签,我也来逐个介绍一下。
header 和 footer 一般都是放在 article 或者 body 的直接子元素,但是标准中并没有明确规定,footer 也可以和 aside,nav,section 相关联(header 不存在关联问题)。
aside 很容易被理解为侧边栏,实际上二者是包含关系,侧边栏是 aside,aside 不一定是侧边栏。
aside 和 header 中都可能出现导航(nav 标签),二者的区别是,header 中的导航多数是到文章自己的目录,而 aside 中的导航多数是到关联页面或者是整站地图。
最后 footer 中包含 address,这是个非常容易被误用的标签。address 并非像 date 一样,表示一个给机器阅读的地址,而是表示“文章(作者)的联系方式”,address 明确地只关联到 article 和 body。
本篇中我们介绍了一些基本原则和 HTML 文档的整体结构,从整体上了解了 HTML 语义。
至此,我们可以回答是否要语义化的问题:我们应该分开一些场景来看语义,把它用在合适的场景下,可以获得额外的效果。本篇文中,我们至少涉及了三个明确的场景:
实际上,HTML 这种语言,并不像严谨的编程语言一样,有一条非此即彼的线。一些语义的使用其实会带来争议,所以我的建议是:你可以尽量只用自己熟悉的语义标签,并且只在有把握的场景引入语义标签。这样,我们才能保证语义标签不被滥用,造成更多的问题。
你最擅长使用哪些语义标签,会把它们用在哪些场景里呢?欢迎留言告诉我,我们一起讨论。