[译] 不要再用这么多 div 了,试试语义化标签吧

4,737 阅读15分钟

原文链接:Stop using so many divs! An intro to semantic HTML,by Ken Bellows

好多 div

我们都喜欢使用 <div> 写网页。它已经存在几十年了。几十年来,当你出于样式或结构的考虑需要将一些东西包装到一起时,<div> 已经成为首选元素。现在的线上网站普遍能看到下面的代码结构:

<div class="container" id="header">
    <div class="header header-main">Super duper best blog ever</div>
    <div class="site-navigation">
        <a href="/">Home</a>
        <a href="/about">About</a>
        <a href="/archive">Archive</a>
    </div>
</div>
<div class="container" id="main">
    <div class="article-header-level-1">
        Why you should buy more cheeses than you currently do
    </div>
    <div class="article-content">
        <div class="article-section">
            <div class="article-header-level-2">
                Part 1: Variety is spicy
            </div>
            <!-- cheesy content -->
        </div>
        <div class="article-section">
            <div class="article-header-level-2">
                Part 2: Cows are great
            </div>
            <!-- more cheesy content -->
        </div>
    </div>
</div>
<div class="container" id="footer">
    Contact us!
    <div class="contact-info">
        <p class="email">
            <a href="mailto:us@example.com">us@example.com</a>
        </p>
        <div class="street-address">
            <p>123 Main St., Suite 404</p>
            <p>Yourtown, AK, 12345</p>
            <p>United States of America</p>
        </div>
    </div>
</div>

用了很多 <div> 啊。不管怎么说,这样用也是有效的。我的意思是说,这是我们需要的结构,而且也能依照设计完成样式写书。但这里面有些大问题:

  1. 可访问性:许多 a11y 工具都很聪明。它们会尽力解析页面结构,帮助用户按照开发者的意图浏览页面结构,并为用户提供轻松的跳转点(jump points),快速导航到页面的某个部分。但 <div> 并不能提供有关文档结构的任何有用信息。世界上最聪明的 a11y 工具说到底也不是人,不能期望它通过解析 class 和 id 就能明白全世界开发者各种怪异和狂野的命名方式。我能知道 class="article-header-level-2" 是一个副标题,但机器人不能。(如果真的可以,请你把它从我的计算机中请出,我还没有做好 AGI 革命 到来的准备。)
  2. 可读性:阅读上面的代码,我们需要仔细查看类名,还要分清楚是哪个 <div class="..."></div> 上的。不过,一旦进入到深几个层次的标记,就很难分清楚哪些 </div> 结束标记对应的 <div...> 开始标记在哪里。然后,我们就非常依赖 IDE 提供的为 不同的缩进级别着色突出显示匹配的标签 等功能,来搞清楚当前所处的位置。如果文档内容很长的话,可能还要借助大量滚动。
  3. 一致性和标准。当开始一项新任务或转移到一个新项目时,从头学习整个代码库中使用的所有疯狂标记约定可能会令人沮丧。如果大家都用一种标准化的方法来标记 Web 文档里的通用结构,那么即使对于不熟悉的代码库,我们也能通过浏览 HTML 文件,快速了它的意思。这个标准就是 HTML5。

HTML5

HTML5 不是什么新东西了。简单介绍一下:它是在 2008 年 1 月(12 年前!)发布了最初的工作草案征求公众意见,并于 2014 年 10 月成为 W3C 推荐标准。也就是说,HTML5 正式存在都 5 年多了。

HTML5 的主要进步之一是引入了标准化的 语义元素。“语义”是指单词或事物的含义,因此“语义元素”就是为了用更有 意义的 方式标记文档结构的元素。语义标签可以清楚地表明元素的 用途 和在文档里所起的作用。而且,由于是标准化的,所以用这些元素定义文档,不仅开发者,就连机器人都能顺利的理解和使用。

其实 HTML5 标准在 定义 <div> 元素时的一个 note 它的使用场景:

NOTE:

除非是没有其他合适的元素了,强烈建议作开发者(Author)将 div 元素视为最后的选择。使用更合适的元素而非 div 可以为读者提供更好的可访问性,而且代码也更易维护。

— www.w3.org/TR/html5/gr…

我将语义块元素分为两类:主要结构(primary structure) 和  指示内容(content indicators)。这些不是标准术语,是我为这篇文章总结的,我认为这种区分更利于我们学习。

主要结构

现在的互联网网站、教程甚至 CSS 库中能够找到一种超级通用的模式。页面的最顶层通常分为三个区域:headermainfooter,这些区域还可以进一步划分成不同的 section。上面示例中代码就证明了我的观点:

<div class="container" id="的">...</div>
<div class="container" id="main">
    ...
    <div class="article-section">...</div>
    ...
</div>
<div class="container" id="footer">...</div>

我已经看过(并使用)这个模式几十年了,以这种方式构造文档非常有意义,既可以提高 HTML 的可读性,也可以简化页面样式。header 和 footer 元素也能与 PHP 或 Rails/ERB 等语言中的部分模板更好的协作使用,比如你可以在整个站点中使用统一页眉和页脚:

<?php include 'header.php'; ?>

<div id="main">...</div>

<?php include 'footer.php'; ?>

大家都觉得这是一个很好的模式,包括在 WHATWG 和 W3C 的朋友们。于是 HTML5 就引入了 4 个看名字就知道其含义的标准化语义元素:<header><main><footer><section>

书立:<header> 和 <footer>

<header><footer> 元素是一对双胞胎:关于可以在文档里的哪里使用的规则在规范里的定义很相似,唯一的区别是:<header> 放在事物的开始,<footer> 放在事物的结尾。所谓“事物”,不仅指页面的 元素:这对元素可以在文档中任何具有清晰开始和结尾的内容块中使用。这些内容块可以是表单、文章、文章中的某个部分、社交媒体网站上的帖子、卡片等。

<header> 和 <footer>  在语义上是在最近的 “secioning root”或“sectioning content”元素里使用的。这些元素由 <body><blockquote><section><td><aside> 和许多其他元素构成(如果需要查看完整的列表,请单击上面的链接)。辅助技术可以使用这些元素和其他元素来生成文档的大纲,帮助用户更轻松地浏览文档。同时需要注意的是,每个 sectioning root/content 只能有一个 <header><footer>(每个最多出现一次,不可能同时两次)。

最后要注意的是,<header> 中通常会包含标题元素(<h1>~<h6>)。当然,不是一定要有的,但可以帮助其他相关元素与标题组合,比如:链接、图像或子标题。即使 <header> 中只包含一个标题元素,也能保持它在结构上的一致。

好东西:<main>

第三个主要的 regoin 元素是 <main>,它比较特殊。规范中对它做了两点重要说明:

文档的 <main> 包含该文档独一无二的内容。它不包括文档中的重复内容,比如网站导航链接、版权信息、网站 Logo、Banner 以及搜索表单(除非文档或应用的主要功能是表单搜索功能)。

www.w3.org/TR/html5/gr…

因此 <main> 中放置的是一个页面里好的、重要的内容,它是用户为什么 访问这个页面的主要原因,而不是你的站点。换句话说,主要内容。😯😲🤯

其他的像 Logo、搜索表单、导航啦这些东西可以放在 <body> 的 <header><footer> 里面,<main> 的外面。

一个文档中最多只能有一个 <main> 元素。如果文档中包含多个 <main> 元素,则其他的必须使用 hidden 属性 隐藏。

www.w3.org/TR/html5/gr…

这很独特。与 <header><footer>(以及大多数的其它块状元素)不同的是,<main> 不能在整个页面里的任意 sectioning content 中使用,它能且只能使用一次。或者可以多次使用,但一次只能让一个 <main> 元素可见,其它的则要通过 hidden 属性隐藏,效果等同于 CSS 的 display: none

细想一下,这个限制对在应用程序添加预加载视图的功能非常有用:创建一个新的 <main hidden>,获取用户接下里可能要查看的内容(例如,系列中的下一篇文章,幻灯片演示里的下一张幻灯片等等)。当用户单击链接/按钮时,通过切换当前 <main> 和预加载的 <main hidden> 中的 hidden 属性实现视图切换功能。

接下去讲之前,我们先暂停一下,回顾下之前讲过的内容。文章开始的代码 ,使用 <header><footer><main> 修改之后,变为下面这样了:

<header>
    <h1>Super duper best blog ever</h1>
    ...
</header>
<main>
    <h2>Why you should buy more cheeses than you currently do</h2>
    ...
</main>
<footer>
    Contact us!
    <div class="contact-info">this.is.us@example.com</div>
</footer>

很好!不过还有很多工作要做。

拆分:<section>

至此,我们得到了页面的基本大纲:页眉、页脚和主要内容区域。现在是时候添加一些美妙的内容了。

我们通常会将内容细分成多个 section,尤其是对下面这样,包含大量文本内容的文章。没有人喜欢阅读这样的文本墙。

没人喜欢读这样的文本墙

这就是引入 <section> 的意义所在。<section> 的规则是最简单的:结构上来说,它基本上就是一个具有特殊的语义的 <div><section> 开启一个新的“sectioning content”区域,所以它可以有自己的 <header><footer>

那么,<section> 和普通 <``div``>之间的区别是什么。什么时候用 <div>,又是什么时候使用 <section> 呢?我再次引用规范来解答:

NOTE:

<section> 不是通用容器元素。如果这是为了样式目的或方便编写脚本代码,那么鼓励开发者使用 <div>。一般规则是,如果元素内容需要出现在文档 大纲 中,那么这些内容就要使用  <section> 元素包装。

www.w3.org/TR/html5/se…

顺便说一句,HTML5 规范很容易阅读。每次为了得到答案快速浏览时,我都会不可避免地学到一些意想不到的有用信息,尤其是在我开始点击链接时。有空试试看吧!

简而言之——如果你需要将这部分内容出现在目录中,那么就使用 <section> 吧;如果不需要,那就使用 <div> 或其他元素吧。

指示内容

我们已经得到了一个结构牢固的页面了。没有只用 <div>,我们明确标记了页面的主要内容区域,还标识了页眉、页脚和 section,但是绝对还有比我们文档中使用的更多语义元素。

接下来,我们再谈谈 HTML5 中添加的其它一些元素,这些元素传达的是 内容 语义而非 结构 语义。

完全独立的内容:<article>

<article> 元素用来表示完全独立的内容区域。这块内容区域即使被放入另一个页面,仍然有意义。比如,这可能是篇文章或帖子,但也可能是一篇推文或者或 Facebook 墙贴之类的社交媒体内容。

HTML5 规范建议 <article> 都要有一个标题,来标识它的含义,最好使用标题元素(<h1>~<h6>)。<article> 元素中可以包含 <header><footer><section> 元素,因此你确实可以用它在另一个页面中嵌入完整的文档片段及其需要的所有结构。

回到最初的例子,我们使用 <article> 和讨论过的一些其他元素来重写 class="article-*" 元素。

<article>
    <header>
        <h1>Why you should buy more cheeses than you currently do</h1>
    </header>
    <section>
        <header>
            <h2>Part 1: Variety is spicy</h2>
        </header>
        <!-- cheesy content -->
    </section>
    <section>
        <header>
            <h2>Part 2: Cows are great</h2>
        </header>
        <!-- more cheesy content -->
    </section>
</article>

是不是可读性多了好多?不只是更易于阅读了,辅助技术也能识别这个结构。机器人不能总是辨别出正确的类名含义,却能理解这些结构。

<nav>

这个元素比其他元素更出名。因为 <nav> 是为了清楚地标识页面上的主要导航块,帮助用户导航到网站的其他部分(比如站点地图或 <header> 中的链接列表)或导航当前页面(比如标识目录)。

上面的例子中,我们为 <header> 中的链接应用 <nav>

<nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/archive">Archive</a>
</nav>

修改没有改变页面结构,不需要看 <div> 的类名,就能一眼知道此处的含义,更重要的是,机器人也能理解了。

取得联系:<address>

我们要讨论的最后一个元素是 <address>。这个元素用来标识联系信息,通常出现在主页的 <footer> 中。比如标记企业的邮寄地址、电话号码、客服邮件等。

有趣的是,如何标记 <address> 元素内容的规则是开放的。规范中提到其他一些规范可以解决了这个问题,并且提供这种粒度级别的内容可能超出 HTML 本身的范围。

常见的解决方案是 RDFa,也是一个 W3C 规范。它使用标签属性来标记数据的不同组成部分。下面是我们示例中的页脚代码在使用 <address> 元素和 RDFa 标记调整后的样子:

<footer>
    <section class="contact" vocab="http://schema.org/" typeof="LocalBusiness">
        <h2>Contact us!</h2>
        <address property="email">
            <a href="mailto:us@example.com">us@example.com</a>
        </address>
        <address property="address" typeof="PostalAddress">
            <p property="streetAddress">123 Main St., Suite 404</p>
            <p>
                <span property="addressLocality">Yourtown</span>,
                <span property="addressRegion">AK</span>,
                <span property="postalCode">12345</span>   
            </p>
            <p property="addressCountry">United States of America</p>
        </address>
    </section>
</footer>

RDFa 的写法确实有点冗长,但它对于标记数据非常方便。如果您想了解有关 RDFa 的更多信息,这里提供了几个链接:

总结

我们到现在已经讲了很多了。把上面修改后的代码全部组合起来,就是下面这样啦。

<header>
    <h1>Super duper best blog ever</h1>
    <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
        <a href="/archive">Archive</a>
    </nav>
</header>
<main>
    <article>
    <header>
        <h1>Why you should buy more cheeses than you currently do</h1>
    </header>
    <section>
        <header>
            <h2>Part 1: Variety is spicy</h2>
        </header>
        <!-- cheesy content -->
    </section>
    <section>
        <header>
            <h2>Part 2: Cows are great</h2>
        </header>
        <!-- more cheesy content -->
    </section>
</article>
</main>
<footer>
    <section class="contact" vocab="http://schema.org/" typeof="LocalBusiness">
        <h2>Contact us!</h2>
        <address property="email">
            <a href="mailto:us@example.com">us@example.com</a>
        </address>
        <address property="address" typeof="PostalAddress">
            <p property="streetAddress">123 Main St., Suite 404</p>
            <p>
                <span property="addressLocality">Yourtown</span>,
                <span property="addressRegion">AK</span>,
                <span property="postalCode">12345</span>   
            </p>
            <p property="addressCountry">United States of America</p>
        </address>
    </section>
</footer>

要我说,现在的写法比开始的写法可读性提高了 100 倍,同时对 SEO 和辅助设备也友好了 100 倍。

当然,HTML 中的语义元素决不止这些,还有许多其他的元素来帮助我们标记和构造文本和嵌入式媒体等。如果你对语义标签的学习还未满足,想要更加深入地了解。这里列出一些供你参考,其中一些也可能已经认识了:

本篇文章只是起了个头。当你开始阅读 HTML 规范时,发现根本停不下来。它是一门非常丰富的语言,我认为大家低估了 HTML 在网站里所起的作用。

(正文完)


广告时间(长期有效)

我有一位好朋友开了一间猫舍,在此帮她宣传一下。现在猫舍里养的都是布偶猫。如果你也是个爱猫人士并且有需要的话,不妨扫一扫她的【闲鱼】二维码。不买也不要紧,看看也行。

(完)