如何对您的 HTML 进行分段

Avatar of Daniel Tonon
Daniel Tonon

DigitalOcean 为您的旅程的每个阶段提供云产品。 立即开始使用 $200 免费信用额度!

HTML5 中的 分段元素<nav><aside><article><section><body> 也是一种分段元素,因为它内部的所有内容都是 默认文档部分的一部分

以下是每个分段元素的简要说明及其使用方式

  • <nav> – 等同于 role="navigation"。 主要网站导航,经常出现在整个网站上。 例如主导航、次级导航和页面内导航。
  • <aside> – 等同于 role="complementary"。 与主要内容仅间接相关(或不相关)的内容。 可以想一下带有补充信息的侧边栏、文章中的注释,或者博客文章底部相关文章列表的外部容器。
  • <article> – 等同于 role="article"。 自包含的内容,即使在脱离上下文时也能独立理解。 这可能意味着一个小部件、一篇博客文章,甚至博客文章中的评论。
  • <section> – 等同于 role="region"。 需要从其父分段元素获取额外上下文才能理解的内容。 这是一个通用分段元素,在其他语义更丰富的元素不合适时使用。

👋 我们在评论中注意到 (以及 Twitter 上),本文中使用的一些技术会导致屏幕阅读器用户体验不佳。 我们将进行更新,以确保本文反映最佳信息。 目前,请查看 Adrian Roselli 的下面 Pen,以了解更合适的示例布局分段方法。 感谢您成为一个如此出色的社区,让我们一起学习和成长!

内容

这是一篇非常长的文章,我怀疑您会想要多次回来参考。 为了便于操作,这里列出了所有文章标题

何时使用 <nav>

每个导航块只需要使用一次 <nav> 元素。 已经包含在 <nav> 元素中的子导航不需要用第二个 <nav> 元素包装。

<nav aria-label="Primary">
  <ul>
    <li><a href="#">Primary link</a></li>
    <li><a href="#">Primary link</a></li>
    <li>
      <a href="#">Primary link</a>
      <!-- <nav> element is *not* needed again here -->
      <ul>
        <li><a href="#">Secondary link</a></li>
        <li><a href="#">Secondary link</a></li>
      </ul>
    </li>
  </ul>
</nav>

<nav> 元素仅用于主要导航块。 不过,“主要”是一个非常主观的术语。 html5doctor.com 对何时使用 <nav> 以及何时不使用 <nav> 做了很好的解释,请记住以下内容是意见,而不是官方的 W3C 裁决

关键短语是“主要”导航。 我们可以整天争论“主要”的定义,但对我来说,这意味着

  • 主导航
  • 网站搜索
  • 次级导航(可能)
  • 页面内导航(例如,在长篇文章中)

虽然这里没有对错之分,但民意调查加上我自己的理解告诉我,以下内容不应该用 <nav> 包裹

  • 分页控件
  • 社交链接(虽然可能有一些例外情况,社交链接是主要导航,例如在 About me 或 Flavours 上)
  • 博客文章中的标签
  • 博客文章中的类别
  • 三级导航
  • 宽页脚

html5doctor.com(删除线由我添加)

面包屑是另一种应该用 <nav> 元素包装的内容,正如 W3C 面包屑 HTML 示例 所示。

在搜索表单周围使用 <nav> 是不必要的

我不同意 HTML5 Doctor 的观点,即网站搜索表单应该用 <nav> 元素包装(因此我划掉了它)。 <nav> 应该用于包装导航链接,而不是表单。 网站搜索实际上有 自己的特殊角色,将其定义为搜索地标。 如果您在搜索 <form> 元素中添加 role="search",它将被宣布为屏幕阅读器用户作为搜索地标。 屏幕阅读器用户还可以在通过地标导航时导航到它。 如果页面上有多个搜索表单,则应使用 aria-labelaria-labelledby 对其进行区分(有关这些属性的更多详细信息,请参阅后面的内容)。 但是,不要在 aria 标签中包含“search”一词,这就像在图像 alt 文本中说“image of”一样;这是不必要的。 相反,重点关注搜索表单正在搜索的内容。 因此,对于全局网站搜索,为其提供 aria-label="site" 将是合适的。

<!-- <nav> is not needed on a search form. -->
<!-- role="search" is enough -->
<form role="search" aria-label="site">
  <label>
    <span>Search</span>
    <input type="search" />
  </label>
  <button type="submit">Submit</button>
</form>

role="search" 表单不会出现在文档大纲中,但我认为这没关系,因为搜索表单通常很小且自包含。 它仍然可以获得使用分段元素带来的大部分好处。 将分段元素添加到其中会导致屏幕阅读器用户收到告知他们这是搜索表单的消息(一个用于分段元素,一个用于搜索角色,一个用于搜索输入标签)。

不要在标签中使用“nav”或“navigation”

role="search" 一样,在 <nav> 元素的标签中添加“navigation”只会导致屏幕阅读器两次说“navigation”。

<nav aria-label="primary navigation">
  <!-- Screen reader: "primary navigation navigation landmark" -->
</nav>

<nav aria-label="primary">
  <!-- Screen reader: "primary navigation landmark" -->
</nav>

需要问自己的问题

同一篇 HTML5 Doctor 文章列出了两个问题,可以帮助您确定某个东西是否应该用 <nav> 包裹。

  • 另一个分段元素是否也适用? 如果是,也许可以使用它代替。
  • 您是否会为它添加一个链接到“跳过”块以实现可访问性? 如果没有,那么可能不值得使用 <nav> 元素。

在那些导航过于次要而无法证明使用 <nav> 元素的案例中,<section> 很可能是您应该改用 的元素。

<nav> 最常见的用例是将它包装在一个链接列表周围,但 它并不必须是链接列表。如果您的导航以其他方式工作,您仍然可以使用 <nav> 元素。

<!-- Example taken from the <nav> element specification -->
<!-- https://html.whatwg.com.cn/multipage/sections.html#the-nav-element:the-nav-element-5 -->
<nav>
  <h1>Navigation</h1>
  <p>You are on my home page. To the north lies <a href="/blog">my
  blog</a>, from whence the sounds of battle can be heard. To the east
  you can see a large mountain, upon which many <a
  href="/school">school papers</a> are littered. Far up thus mountain
  you can spy a little figure who appears to be me, desperately
  scribbling a <a href="/school/thesis">thesis</a>.</p>
  <p>To the west are several exits. One fun-looking exit is labeled <a
  href="https://games.example.com/">"games"</a>. Another more
  boring-looking exit is labeled <a
  href="https://isp.example.net/">ISP™</a>.</p>
  <p>To the south lies a dark and dank <a href="/about">contacts
  page</a>. Cobwebs cover its disused entrance, and at one point you
  see a rat run quickly out of the page.</p>
</nav>

同样,在 <nav> 元素中放置一些像简介文本这样的小片段是可以的,只要内容的主要重点是导航链接。简介内容最好放在 <nav> 元素中的 <header> 内。我将在后面更详细地介绍标题和页脚。

<nav>
  <header>
    <h2>In this section</h2>
    <p>This is some intro text describing what you will find in this section.</p>
  </header>
  <ul>
    <li><a href="#">Sub section one</a></li>
    <li><a href="#">Sub section two</a></li>
  </ul>
</nav>

避免将 <aside> 嵌套在 <aside>

就像 <nav> 永远不应该嵌套在另一个 <nav> 元素内部一样,<aside> 元素也往往不会相互嵌套。<aside> 用于表示与周围内容略有相关的内容。这意味着将一个 aside 放在另一个 aside 内基本上是在宣布一个与本身就是一个与主要内容无关的内容无关的切线。

<!-- Don't do this -->
<aside aria-label="Side bar">

  <aside>
    <h2>Share</h2>
    <ul>
      <!-- List of social media links -->
    </ul>    
  </aside>

  <aside>
    <h2>Recommendations:</h2>
    <ul>
      <li>
        <article>
          <h2><a href="#">Related article title</a></h2>
          <p>Article description</p>
        </article>
      </li>
      <!-- List of recommended articles continues -->
    </ul>
  </aside>

</aside>

如果您有一个具有多个部分的侧边栏,请不要像上面的示例那样将 <aside> 元素嵌套在 <aside> 元素内部。相反,将侧边栏设为单个 <aside>,然后使用 <section>(或其他合适的节元素)来创建不同的部分。

<!-- Do this instead -->
<aside aria-label="Side bar">

  <section>
    <h2>Share</h2>
    <ul>
      <!-- List of social media links -->
    </ul>    
  </section>

  <section>
    <h2>Recommended articles:</h2>
    <ul>
      <li>
        <article>
          <h2><a href="#">Related article title</a></h2>
          <p>Article description</p>
        </article>
      </li>
      <!-- List of recommended articles continues -->
    </ul>
  </section>

</aside>

文章就像“块”;节就像“元素”

<section><article> 很容易混淆。如果您熟悉“块元素修饰符”(BEM)语法,那么区分这两者的一个简单方法是 <article> 就像 BEM 中的“B”(或“块”)。它是一个容器,存储自包含的内容,即使放在不同的上下文中仍然有意义。Twitter 上的单个推文和搜索结果页面上的每个列表项都被视为 <article> 元素。

<section> 就像 BEM 中的“E”(或“元素”)。它是一个子部分,需要来自其父节元素的上下文才能有意义。<section> 是一个通用的通用节元素,当使用其他节元素没有意义时使用它。所以,如果有疑问,就用 <section>

请注意,如果某事物在 BEM 中被样式化为“块”,这并不意味着它一定是 <article> 元素。BEM “元素”和 <section> 元素也是如此。事物的元素应该基于内容的含义,而不是内容的外观

评论部分

可能令人们惊讶的是,博客文章上的单个评论 也被视为文章,即使它们是对主要博客文章的回复。围绕主要博客文章的 <article> 元素也应该围绕评论部分。这是为了表示评论与主要文章有关。

<article>
  <h1>I am an awesome blog post!</h1>
  <p>I am some body text content.</p>

  <section>
    <h2>Comments</h2>
    <ul>
      <li>
        <article>
          <h2>Username</h2>
          <p>This is the comment body text.</p>
          <footer>
            <p>
              Meta data like post date makes sense
              in either the header or the footer.
            </p>
          </footer>
        </article>
      </li>
    </ul>
  </section>
</article>

不要将 div 替换为 section

仅仅因为我们现在有了这些花哨的节元素,并不意味着旧的 <div> 元素已经失去了所有用处。<div> 没有语义含义,因此当我们纯粹为了样式目的而更改 HTML 时,它非常有用。

假设我们有一个包含在 <article> 元素内的博客文章,我们需要用一些东西将其包装起来以进行样式设置。

<!-- I need a wrapper element -->
<article>
  <h1>I am a blog post</h1>
  <p>I am some content</p>
</article>

在这种情况下,使用 <section> 元素是不正确的。

<!-- Do not do this -->
<section class="wrapper">
  <article>
    <h1>I am a blog post</h1>
    <p>I am some content</p>
  </article>
</section>

尽管 <section> 从技术上讲是一个通用元素,但在这种情况下,<div> 是更合适的选择。这个新的包装容器不应包含任何语义含义,而这正是 <div> 的设计目的。

<!-- Use a <div> if the element is only used for styling purposes -->
<div class="wrapper">
  <article>
    <h1>I am a blog post</h1>
    <p>I am some content</p>
  </article>
</div>

记住这一点的另一个方法是:如果您无法想到要应用于 <section> 的有意义的标题,那么它可能不应该是一个 <section>

标题和页脚

虽然它们并不一定需要,但节元素可以包含一个 <header>一个 <footer>,标题位于节的顶部,页脚位于节的底部。

A basic sectioning element with a header and a footer.

节元素可以根据内容的需要相互嵌套任意多次。

Nested sectioning elements

节元素中的标题和页脚也可以包含节元素。

Headers and footers containing sectioning elements

围绕嵌套节元素的一个主要限制是,标题和页脚 不能嵌套在其他标题和页脚内部

不允许将标题和页脚相互嵌套。

标题内放什么?

标题用于介绍性内容。<header> 元素中适当包含的内容包括(但不限于)

  • 标题元素 (<h1><h6>)
  • 简介段落或语句。
  • 个人资料图片
  • 徽标
  • 搜索表单
  • 主导航
  • 作者姓名
  • 发布/更新日期
  • 元数据
  • 社交媒体链接

页脚内放什么?

页脚元素主要用于存放元数据和次要支持性内容。<footer> 元素中适当包含的内容包括(但不限于)

  • 版权信息
  • 法律事项
  • 脚注
  • 低优先级的网站导航
  • 作者姓名
  • 发布/更新日期
  • 元数据
  • 社交媒体链接

您会注意到,在标题和页脚之间,一些内容在两者中都很合适。这主要是因为元类型内容适合放在这两个元素中的任何一个。这主要取决于您试图实现的设计。不过,<header> 元素往往表示它们内部的内容比 <footer> 元素内部的内容更重要。

节元素和文档大纲算法

了解这些节元素的一个重要事项是,它们都应该在其内部包含一个 <h#> 元素(或以其他方式进行标记,但我们稍后会详细介绍)。这主要为了某种名为 文档大纲算法 的东西。这是一种算法,它使用节元素来帮助确定标题 (<h#>) 的级别,而不必完全依赖开发人员提供的数字。所以,您是否曾经想过在一个页面上拥有多个 <h1> 是否可以?这旨在使它不再是一个问题(但稍等一下,因为还有更多内容)。

<!-- No Document outline algorithm -->
<article>
  <h1>Primary heading</h1>

  <h2>Secondary heading</h2>
  <p>Some text.</p>

  <h3>Tertiary heading</h3>
  <p>Some text.</p>
</article>
<!-- With document outline algorithm -->
<article>
  <h1>Primary heading</h1> <!-- Recognized as <h1> -->
  
  <!-- sectioning element sets new heading level context -->
  <section>
    <h1>Secondary heading</h1> <!-- Recognized as <h2> -->
    <p>Some text.</p>

    <h2>Tertiary heading</h2> <!-- Recognized as <h3> -->
    <p>Some text.</p>
  </section>

</article>

关于文档大纲算法,还有很多东西要学。但我在这里就停下来了,因为…

没有浏览器支持文档大纲算法

没有一个浏览器支持这种创建标题结构的方法。这太可惜了。如果我们不必总是担心使用正确的标题级别,构建无障碍网站会容易得多。

据我所知,没有浏览器实现该算法的主要原因有两个。一个是浏览器供应商害怕破坏使用分区元素不正确网站的标题结构。另一个原因是,当前文档大纲算法规范很难实现,还没有浏览器供应商愿意投入时间去实现它。

关于第一个原因,有一个长篇讨论,讨论的是是否应该使用新的<h>元素而不是使用<h1>元素来告诉浏览器使用文档大纲算法。我支持使用新的<h>元素的想法,直到我意识到,在<html>元素上添加一个属性或在<head>中添加一个<meta>标签,作为告诉浏览器使用该算法的安全方法,效果会更好。如果在不支持的浏览器中,标题回退到<h1>,也比回退到<span>更好。

如果你想尝试一下这个<h>概念,有一个名为hfill的插件。它允许你在分区元素中嵌套<hx>标题,从而创建文档大纲,而不用过多地担心标题级别。有一个演示供你试用。不过,这个插件的主要缺陷是,唯一可以提高标题级别的途径是将分区元素嵌套在彼此内部。在这个插件中,没有<h1>大于<h2>的动态,这也是我最终放弃这个<h>元素想法的主要原因。这种缺乏标题层次结构会使 CMS 富文本编辑器对于客户来说太难使用。

至于实现难度问题,目前正在努力制定一个简化规范,浏览器供应商更有可能采用这个规范。文档大纲算法已经出现在 HTML 规范中多年了。希望这个简化的规范能让该算法成为现实。

虽然该算法目前还没有在任何地方得到支持,但我们仍然可以以该算法为目标进行构建。如果我们以该算法为目标进行构建,那么我们将获得以下好处:

  1. 如果该算法确实得到实施,我们可以使我们的网站面向未来。
  2. 我们可以显著提升屏幕阅读器用户的用户体验。
  3. 我们可能会提高搜索引擎优化 (SEO),因为搜索引擎能够更好地理解网站的内容。
  4. 我们可以通过让用户使用利用分区元素的原生浏览器功能(如阅读器模式)来改善用户体验。

分区内容

看看我整理的这个模拟布局,想想你可能会如何将其分成几个部分。

Mock-up layout featuring a logo, primary nav, and search in the header, a secondary navigation in a left sidebar, and a main content area  in the middle with primary and secondary headings, a sidebar on the right with share links and links to recommended articles.

我会这样将布局分成分区元素(只有实线代表分区元素)。

Layout showing where various sectioning elements would appear.

在 HTML 标记方面,它看起来像这样

<body>

  <header>
    <a href="/" title="Go to home page">
      <img src="logo.png" alt="Site logo">
    </a>
    <nav>
      <ul>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
      </ul>
    </nav>
    <form role="search" aria-label="site">
      <label>
        <span>Search</span>
        <input type="search"/>
      </label>
      <button type="submit">Submit</button>
    </form>
  </header>

  <nav>
    <ul>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
    </ul>
  </nav>

  <main>
    <article>
      <h1>Main article heading</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat. Unum est sine dolore esse, alterum cum voluptate. Laboro autem non sine causa; Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Nihil opus est exemplis hoc facere longius. Duo Reges constructio interrete. Graecum enim hunc versum nostis omnes Suavis laborum est praeteritorum memoria. Haec et tu ita posuisti, et verba vestra sunt.</p>

      <h2>Article secondary heading</h2>
      <p>Nos commodius agimus. A mene tu? Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. Tenesne igitur, inquam, Hieronymus Rhodius quid dicat esse summum bonum, quo putet omnia referri oportere? Nihilo beatiorem esse Metellum quam Regulum. Sed quanta sit alias, nunc tantum possitne esse tanta. Philosophi autem in suis lectulis plerumque moriuntur. Esse enim, nisi eris, non potes.</p>
      <p>Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari. Id Sextilius factum negabat. Quorum sine causa fieri nihil putandum est. Quae autem natura suae primae institutionis oblita est?</p>
    </article>
  </main>

  <aside>
    <section>
      <h2>Share</h2>
      <ul>
        <li><a href="#">Facebook</a></li>
        <li><a href="#">Twitter</a></li>
        <li><a href="#">Email</a></li>
      </ul>
    </section>
    <section>
      <h2>Recommended</h2>
      <ul>
        <li>
          <article>
            <h3><a href="#">Related article</a></h3>
            <p>Article description</p>
          </article>
        </li>
        <li>
          <article>
            <h3><a href="#">Related article</a></h3>
            <p>Article description</p>
          </article>
        </li>
      </ul>
    </section>
  </aside>

  <footer>
    <ul>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
    </ul>
  </footer>

</body>

<main>元素

在上面的标记中,我使用了一个非常重要的语义元素,但还没有讲过,那就是<main>元素。<main>元素代表页面上的主要内容。它不应包含任何侧边栏或导航元素。你也不应该在页面上拥有超过一个<main>元素,除非页面上所有其他<main>元素都应用了hidden属性(这是为了 SPA)。

<main>元素不是分区元素。这意味着它不有助于文档大纲算法,它不能以直接子元素的形式包含<header><footer>元素。不过,它是一个地标元素,因此屏幕阅读器用户能够非常轻松地导航到它。

我不确定在<main>元素中使用<article>是否必要。从语义上讲,这是合理的。主要内容是自包含的,因此以这种方式使用<article>元素是合理的。从文档大纲算法的角度来看,<article>元素也有助于文档结构。

从可用性的角度来看,它感觉有点不必要,而且文档大纲算法目前还没有在任何地方生效。我将在我的示例中继续使用它,但我很想知道其他人对这个想法有什么看法,欢迎在评论区留言。

你需要给你的部分打标签。这里有三种方法。

在本文中,我会经常说“标签”这个词。请记住,我指的是<label>元素。<label>元素不用于标记分区元素。

分区元素需要标签,以便屏幕阅读器用户能够快速识别在网站的特定部分中可以找到哪些内容。我认为在没有提供相关部分标签的情况下使用分区元素是一种无障碍失败,除非它是页面上唯一的同类元素。还建议,不要在多个分区元素(或标题元素)上使用完全相同的标签文本。这使得每个部分对屏幕阅读器用户来说更易于识别,从而帮助他们更轻松地浏览网站。

标记分区元素有三种方法。在下面的示例中,我将“运输”和“可移植性”作为一种方法来解释将部分保存到组件并将其在多个不同上下文中多次使用是多么容易。

我还提供了示例中正负面的列表。在这些列表中,我假设你希望部分标签能够被屏幕阅读器读取,但隐藏在有视力的人面前。

方法 1:添加 aria-label 属性

这是标记分区元素最快最简单的方法。

<section aria-label="Label for this section">
  <p>Content for this section</p>
</section>
aria-label 翻译问题

aria-label 的主要缺点(在撰写本文时)是,大多数浏览器无法为使用与你不同的语言的用户翻译这些值。Google 的开发者最近修复了 Chrome 中的这个错误,但对于所有其他浏览器来说,这个问题仍然存在

如果你的网站拥有庞大的国际受众,或者你知道你的许多用户不使用你的语言,那么你应该在所有浏览器都支持翻译此属性之前,避免使用此属性。如果你没有这类用户,可以相当安全地假设,浏览你网站的非视力用户能够理解你的语言——至少能够理解到能够浏览你的网站的程度。

如果你需要更多说服,假设你的网站国际用户很少。这意味着你的用户通常来自与你同一个国家。如果他们来自同一个国家,那么他们很可能与你使用相同的语言,因此,只有一小部分用户无法理解你网站的本地语言。现在考虑一下,aria-label 仅影响屏幕阅读器用户。现在,这仅仅是你网站上已经很小比例的用户的很小一部分。现在再考虑一下,Chrome(全球最受欢迎的浏览器)现在支持翻译aria-label属性。用户必须同时没有使用最新版本的 Chrome 作为浏览器,翻译问题才会成为问题。如果你将所有这些因素加在一起,很有可能你没有任何用户既能够感知到aria-label属性,又无法理解它们的含义。这让我觉得,aria-label 的糟糕的多语言支持实际上并不值得过分担心,除非你拥有庞大的国际受众,或者你有许多你所知道的不会使用你的语言的用户。

优点
  • 实现起来非常快且容易。
  • 不影响标题结构。
  • 使组件易于运输。
  • 对有视力的人来说是不可见的。
负面影响
  • 在非 Chrome 浏览器中不会翻译成其他语言(截至撰写本文时)。
  • 页面结构分析工具通常不支持。
  • 由于不知道该部分内其他标题的级别,可能会造成混淆。

方法 2:添加一个<h#>元素

<h#>指的是<h1><h2><h3><h4><h5><h6>,具体取决于哪个最合理。在分节元素中添加标题是标记它的一个快速方法。

标题位置

标题可以放在分节元素中,像这样

<section>
  <h1>Heading</h1>
  <p>content</p>
</section>

…或者放在<header>元素内

<section>
  <header>
    <h1>Heading</h1>
    <p>I'm a byline</p>
  </header>

  <p>Content</p>
</section>

您也可以在分节元素和标题之间放置任意多个<div>包装元素。

<!-- This is perfectly fine -->
<section>
  <div>
    <header>
      <div>
        <h1>Heading</h1>
        <p>I'm a byline</p>
      </div>
    </header>

    <p>Content</p>
  </div>
</section>
每个分节元素只有一个最高级别的标题

每个分节元素实际上应该只有一个最高级别的标题。规范中规定,当有多个顶级标题或级别高于第一个标题的标题时,浏览器应该关闭之前的分节元素并开始一个相同类型的新的分节元素。

标题内容元素中,分节内容元素的第一个元素代表该显式部分的标题。后续级别相等或更高的等级标题会开始新的隐式子部分,这些子部分是先前部分的父部分的一部分。后续级别较低的等级标题会开始新的隐式子部分,这些子部分是先前子部分的一部分。在这两种情况下,该元素代表隐式部分的标题。

—HTML 5.3,标题和部分

实际上,浏览器会使用第一个标题作为部分标签,但这些隐式部分永远不会创建。当遇到它时,它只会按照原样宣布标题。这并不糟糕,但确实有点令人困惑。

<!-- Avoid this: -->
<section>
  <h2>Heading level two labeling a section</h2>
  <p>Content</p>

  <!-- Don't use same level or higher headings as the one labeling the section -->
  <h2>This is also a heading level two</h2>
  <p>Content</p>
</section>

<!-- Do this instead: -->
<div>
  <section>
    <h2>Heading level two labeling a section</h2>
    <p>Content</p>
  </section>
  <section>
    <h2>Heading level two labeling a different section</h2>
    <p>Content</p>
  </section>
</div>
标题始终放在最前面

如果分节元素具有<h#>元素,则该顶级标题应始终是该分节元素内的第一个内容。未能做到这一点被视为无障碍性失败

如果您发现需要在标题之前放置内容(例如,图像),您可以使用 Flexbox 重新排列视觉顺序。这将使其看起来像图像在标题之前,但在标记中,标题在图像之前。IE 中有一个错误,有时会导致文本在flex-direction: column;元素中无法换行。您可以通过对 flex-child 元素应用最大宽度来解决此问题。

<!-- Don't do this -->
<section>
  <img src="image.jpg" alt="Don't place content or images before the heading" />
  <h2>Headings should always come first</h2>
  <p>Place regular content after the heading</p>
</section>

<!-- Do this instead -->
<section class="example">
  <h2>Headings should always come first</h2>
  <img src="image.jpg" alt="Don't place content or images before the heading" />
  <p>Place regular content after the heading</p>
</section>

<style>
.example {
  display: flex;
  flex-direction: column;
}
.example img {
  order: -1;
}
</style>

请注意,重新排列视觉顺序以满足 WCAG 指南1.3.2:有意义的顺序可能会直接与 WCAG 指南2.4.3:焦点顺序发生冲突。例如,如果该图像是一个指向文章的链接,而您要放置在它上面的标题也是指向文章的链接,则先放置标题会破坏焦点顺序。先放置图像会破坏有意义的顺序。

在这种情况下,这两个指南相互冲突,我的观点是,如果您无法以某种方式解决冲突,那么1.3.2:有意义的顺序指南是更重要的指南。焦点顺序失败会导致用户在使用 Tab 键浏览内容时感到不舒服,因为焦点会转移到意外的位置。未能遵循有意义的顺序会导致用户感到困惑,无法确定不同内容之间的关系。

将标题制作成视觉上隐藏的部分标签

默认情况下,标题对有视力的人是可见的。如果您希望标题可见,那么这会让标题非常有用。但很多时候,我们并不希望分节元素的标签可见。为了阻止有视力的人看到标签,我们需要使用一些 CSS。

<style>
.visually-hidden {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
</style>

<section>
  <h1 class="visually-hidden">Heading</h1>
  <p>content</p>
</section>
标题受到结构分析工具的良好支持

标题对开发人员来说也具有巨大的优势,因为您可以找到的任何页面结构分析工具都将支持它们。这使得标题结构易于测试和调试。另外两种部分标记方法在测试工具中几乎没有支持。甚至官方 W3C 验证器服务目前也不支持这些替代方法。我发布了一个问题以解决这个问题——如果您精通 Java 编码,请考虑帮助解决这个问题。

优点
  • 快速实现。
  • 可靠地出现在页面结构分析工具中,使其易于测试和调试。
  • 所有浏览器都会将文本翻译成其他语言。
  • 不会对该部分内其他标题的级别造成混淆。
负面影响
  • 影响文档标题结构。
  • 需要确保标题在使用之前处于正确的级别。
  • 默认情况下对用户可见。
  • 需要 CSS 将标题隐藏在视觉用户面前。
  • 可能会因为标题结构要求而导致组件的可移植性降低。

方法 3:使用 aria-labelledby 属性

这是使用aria-labelledby创建隐藏的部分标签的外观。

<section aria-labelledby="unique-id">
  <div hidden id="unique-id">Label for this section</div>
  <p>Content for this section</p>
</section>
标签可以在没有 CSS 的情况下隐藏

请注意,我在示例中使用了hidden属性来隐藏 div,而不是visually-hidden CSS 类。aria-labelledby能够朗读通常对屏幕阅读器用户隐藏的文本。这增加了额外的效果,可以防止屏幕阅读器两次朗读文本。但不要使用aria-hidden属性。屏幕阅读器不会找到标签文本。好吧,当我测试NVDA时,它无法找到标签文本。我不确定其他屏幕阅读器是否可以找到。

主要的可移植性问题

aria-labelledby是所有部分标记方法中最难使用的。使其难以使用的主要方面是aria-labelledby属性依赖于 ID。每当涉及到 ID 时,事情就会变得更加复杂。这是因为网页在任何时候都只能在页面上有一个 ID 实例。这使得组件的可移植性变得困难。

由于存在可移植性问题,我真的只建议在您需要支持多语言受众并且不想搞乱标题结构的情况下使用此选项。

无需将标签放在分节元素附近

您无需将带有标签文本的元素放置在它所标记的分节元素内部或附近。 标签文本可以放置在与分节元素完全不同的位置。 这是由于 ID 将两个元素链接在一起。 我并不一定说这样做是个好主意,但这是您应该了解的 aria-labelledby 的一个功能。

<div hidden id="unique-id">Label for this section</div>

<!-- 1,000 lines of other HTML -->

<section aria-labelledby="unique-id">
  <p>Content for this section</p>
</section>
将非标题元素变成节标签

您可能想要使用 aria-labelledby 的另一个关键原因是。 如果您在页面上有一个可见的非标题元素,您想将其用作某个节的标签,那么 aria-labelledby 非常适合这种情况。 <fieldset> 内部的 <legend> 元素就是一个常见的用例。 这并不意味着您 *必须* 将字段集包装在分节元素中。 我只是在您发现需要的时候提醒您这一点。

<section aria-labelledby="section_label">
  <fieldset>
    <legend id="section_label">
      I am both the fieldset legend and the section label
    </legend>

    <!-- Form fields go here -->
  
  </fieldset>
</section>
优点
  • 所有浏览器都会将文本翻译成其他语言。
  • 可以将非标题元素指定为节标签。
  • 标签的文本不需要放在它所标记的节附近。
负面影响
  • 需要使用 ID 来工作。
  • 难以迁移。
  • 可能很难在源代码中追踪到标签文本的存储位置。
  • 文本默认情况下是可见的,除非使用 hidden 属性。
  • 如果文本没有隐藏,某些屏幕阅读器可能会将文本读出两次。
  • 由于不知道该部分内其他标题的级别,可能会造成混淆。

一次只使用一种方法

不要在同一个分节元素上同时使用 <h#>aria-label 和/或 aria-labelledby 属性。 每个分节元素一次只使用一种标记方法。 使用多种方法会非常混乱,并会导致标签被覆盖。 这就像在 CSS 中两次声明同一个属性一样。 我不确定屏幕阅读器会如何处理这种情况,所以我创建了一个最荒谬的 <section> 并用 NVDA 运行它。

<!-- Don't do this -->
<section aria-label="Is this the section label?" aria-labelledby="is_this_the_label">
  <h1>Or is this the section label?</h1>
  <p id="is_this_the_label">Only ever use one at a time.</p>
</section>

这是 NVDA 对各种标记方法的优先级排序,从最强到最弱

  1. aria-labelledby
  2. aria-label
  3. <h#>

在示例布局中添加节标签

长期以来,我一直使用标题作为标记节的唯一手段。 aria-label 提供的多语言支持很糟糕,aria-labelledby 太过繁琐,无法成为我的主要标记方法。 如果我们 *只* 使用标题来标记节,就会遇到一些问题。 我会告诉你我的意思。

<style>
  .visually-hidden {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }
</style>

<body>

  <header>
    <a href="/" title="Go to home page">
      <img src="logo.png" alt="Site logo">
    </a>
    <nav>
      <h2 class="visually-hidden">Primary</h2>
      <ul>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
      </ul>
    </nav>
    <form role="search" aria-label="site">
      <label>
        <span>Search</span>
        <input type="search"/>
      </label>
      <button type="submit">Submit</button>
    </form>
  </header>

  <nav>
    <h2 class="visually-hidden">Secondary</h2>
    <ul>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
    </ul>
  </nav>

  <main>
    <article>
      <h1>Main article heading</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat. Unum est sine dolore esse, alterum cum voluptate. Laboro autem non sine causa; Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Nihil opus est exemplis hoc facere longius. Duo Reges constructio interrete. Graecum enim hunc versum nostis omnes Suavis laborum est praeteritorum memoria. Haec et tu ita posuisti, et verba vestra sunt.</p>

      <h2>Article secondary heading</h2>
      <p>Nos commodius agimus. A mene tu? Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. Tenesne igitur, inquam, Hieronymus Rhodius quid dicat esse summum bonum, quo putet omnia referri oportere? Nihilo beatiorem esse Metellum quam Regulum. Sed quanta sit alias, nunc tantum possitne esse tanta. Philosophi autem in suis lectulis plerumque moriuntur. Esse enim, nisi eris, non potes.</p>
      <p>Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari. Id Sextilius factum negabat. Quorum sine causa fieri nihil putandum est. Quae autem natura suae primae institutionis oblita est?</p>
    </article>
  </main>

  <aside>
    <h2 class="visually-hidden">Sidebar</h2>
    <section>
      <h3>Share</h3>
      <ul>
        <li><a href="#">Facebook</a></li>
        <li><a href="#">Twitter</a></li>
        <li><a href="#">Email</a></li>
      </ul>
    </section>
    <section>
      <h3>Recommended</h3>
      <ul>
        <li>
          <article>
            <h4><a href="#">Related article</a></h4>
            <p>Article description</p>
          </article>
        </li>
        <li>
          <article>
            <h4><a href="#">Related article</a></h4>
            <p>Article description</p>
          </article>
        </li>
      </ul>
    </section>
  </aside>

  <footer>
    <ul>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
    </ul>
  </footer>
</body>

如果我们现在看一下我们的标题结构,它看起来会像这样(*斜体* = 视觉隐藏;**粗体** = 可见)

  • <h2> Primary [nav]
  • <h2> Secondary [nav]

请注意,我们的 <h1> 标题不在列表的顶部? 在 <h1> 标题之上有两个 <h2> 标题感觉确实不对。

这种标题结构实际上是 W3C 允许的,所以它不算作无障碍失败。 但是,我认为对于屏幕阅读器用户来说,这是一种非常糟糕的 UX。 从 <h1><h2> 并不是一个逻辑上的进展。 如果页面上遇到的第一个标题是 <h1>,然后依次进入 <h2><h3> 等等,这是最有意义的。

使标题 1 成为第一个标题

很长一段时间里,我一直认为处理这种困境的最佳方法是将 <h1> 视觉隐藏,并将其作为页面的第一个内容。 每个人都认为是 <h1> 的东西实际上变成了 <h2>

这就是这种结构在实践中的样子

<style>
  .visually-hidden {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }
</style>

<!-- Don't do this -->
<body>
  <header>
    <h1 class="visually-hidden">Main article heading</h1>
    <a href="/" title="Go to home page">
      <img src="logo.png" alt="Site logo">
    </a>
    <nav>
      <h2 class="visually-hidden">Primary</h2>
      <ul>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
      </ul>
    </nav>
    <form role="search" aria-label="site">
      <label>
        <span>Search</span>
        <input type="search"/>
      </label>
      <button type="submit">Submit</button>
    </form>
  </header>

  <nav>
    <h2 class="visually-hidden">Secondary</h2>
    <ul>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
    </ul>
  </nav>

  <main>
    <article>
      <h2><span class="visually-hidden">Body:</span> Main article heading</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat. Unum est sine dolore esse, alterum cum voluptate. Laboro autem non sine causa; Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Nihil opus est exemplis hoc facere longius. Duo Reges constructio interrete. Graecum enim hunc versum nostis omnes Suavis laborum est praeteritorum memoria. Haec et tu ita posuisti, et verba vestra sunt.</p>

      <h3>Article secondary heading</h3>
      <p>Nos commodius agimus. A mene tu? Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. Tenesne igitur, inquam, Hieronymus Rhodius quid dicat esse summum bonum, quo putet omnia referri oportere? Nihilo beatiorem esse Metellum quam Regulum. Sed quanta sit alias, nunc tantum possitne esse tanta. Philosophi autem in suis lectulis plerumque moriuntur. Esse enim, nisi eris, non potes.</p>
      <p>Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari. Id Sextilius factum negabat. Quorum sine causa fieri nihil putandum est. Quae autem natura suae primae institutionis oblita est?</p>
    </article>
  </main>

  <aside>
    <h2 class="visually-hidden">Sidebar</h2>
    <section>
      <h3>Share</h3>
      <ul>
        <li><a href="#">Facebook</a></li>
        <li><a href="#">Twitter</a></li>
        <li><a href="#">Email</a></li>
      </ul>
    </section>
    <section>
      <h3>Recommended</h3>
      <ul>
        <li>
          <article>
            <h4><a href="#">Related article</a></h4>
            <p>Article description</p>
          </article>
        </li>
        <li>
          <article>
            <h4><a href="#">Related article</a></h4>
            <p>Article description</p>
          </article>
        </li>
      </ul>
    </section>
  </aside>

  <footer>
    <ul>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
    </ul>
  </footer>
</body>

现在我们的文档大纲看起来像这样(*斜体* = 视觉隐藏;**粗体** = 可见)

  • <h1> Main article heading
    • <h2> Primary [nav]
    • <h2> Secondary [nav]
    • <h2> Body: Main article heading
      • <h3> Article secondary heading
    • <h2> Sidebar
      • <h3> Share
      • <h3> Recommended
        • <h4> Related article
        • <h4> Related article

这感觉基本上是正确的。 <h1> 位于顶部,并且所有内容都完美地向下流动,<h2> 元素表示主要页面节,<h3> 元素表示子节。 主要尴尬的地方是,实际的 <h1> 和每个人都认为是 <h1> 的东西本质上是彼此的副本。

直到我写了这篇文章的第一个版本,差点发布了,然后又把它扔掉了,我才开始换个角度思考。 我和两位无障碍顾问讨论了这个问题。 他们都同意,虽然这是一个巧妙的技术解决方案,但它会损害它试图帮助的人的体验。

问题是,当世界上其他所有网站都将 <h1> 标题放在主要内容区域的顶部时,这就是屏幕阅读器用户所期望的。 当您的网站是与众不同的特殊雪花时,它会让屏幕阅读器用户感到困惑,他们需要花一些时间才能弄清楚您的标题结构应该如何工作。

因此,考虑到这一点,我已经确定了一种新的方法来处理分节元素的标记。 基本上,无论何时我使用视觉隐藏的标题,我现在都会使用 aria-label 属性。 如果网站有大量非母语用户,我将使用 aria-labelledby 而不是 aria-label

关于简化大纲算法规范的疑虑

如果 简化大纲算法 按当前状态获批,实际上我们将需要像视觉隐藏的 <h1> 示例一样开始构建我们的网站(只需将 <h2><h3><h4> 元素替换为 <h1> 元素)。

原始规范 旨在通过标记分节元素来创建大纲。 这个新规范显然旨在尝试纯粹通过标题级别来创建大纲。 该算法基本上根据 标题的祖先分节元素的数量 *加上* 标题的基本标题级别值来计算标题级别。 规范中比这更细致,但这是它在简单术语中的工作原理的总体思路。

简化算法目前没有提到 aria-labelaria-labelledby。 这意味着这些属性将不会帮助生成简化算法生成的大纲。 由于缺乏对 aria-label 的支持,这意味着使用 aria-label 标记分节元素可能会导致树中更深层次的标题级别被跳过。

<!-- Simplified algorithm skipped heading levels issue -->
<body>
  <main>
    <h1>Primary heading for the page</h1> <!-- interpreted as <h1> -->
    <p>This is some content</p>
  </main>

  <!-- sectioning elements increase heading levels -->
  <aside aria-label="Side bar"> <!-- aria-label does not contribute -->
    <section>
      <h1>Share</h1> <!-- interpreted as <h3> -->
      <ul>
        <!-- list of social media links -->
      </ul>
    </section>

    <section>
      <h1>Recommended articles:</h1>  <!-- interpreted as <h3> -->
      <ul>
        <!-- list of recommended articles -->
      </ul>
    </section>
  </aside>
</body>

简化规范还认为以下情况是 **无效的**

但是,它允许在文档的根目录处存在 多个级别 1 标题,我发现这一点非常奇怪,对无障碍性不利(虽然我对这一点的担忧 似乎被忽略了)。

我在 GitHub 讨论 中表达了我对规范的意见,并提出了可能的解决方案。

目前,最好还是使用aria-label和/或aria-labelledby属性来标记分节元素,而不是使用视觉隐藏的标题。为了一个还没有最终确定或被接受的规范而降低我们当今用户的体验是不值得的。

在示例布局分节元素上使用 aria

使用aria-label

如果我们使用aria-label属性来标记分节元素,HTML 结构看起来就像这样。

<body>
  <header>
    <a href="/" title="Go to home page">
      <img src="logo.png" alt="Site logo">
    </a>
    <nav aria-label="Primary">
      <ul>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
      </ul>
    </nav>
    <form role="search" aria-label="site">
      <label>
        <span>Search</span>
        <input type="search"/>
      </label>
      <button type="submit">Submit</button>
    </form>
  </header>

  <nav aria-label="Secondary">
    <ul>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
  </ul>
  </nav>

  <main>
    <article>
      <h1>Main article heading</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat. Unum est sine dolore esse, alterum cum voluptate. Laboro autem non sine causa; Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Nihil opus est exemplis hoc facere longius. Duo Reges constructio interrete. Graecum enim hunc versum nostis omnes Suavis laborum est praeteritorum memoria. Haec et tu ita posuisti, et verba vestra sunt.</p>

      <h2>Article secondary heading</h2>
      <p>Nos commodius agimus. A mene tu? Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. Tenesne igitur, inquam, Hieronymus Rhodius quid dicat esse summum bonum, quo putet omnia referri oportere? Nihilo beatiorem esse Metellum quam Regulum. Sed quanta sit alias, nunc tantum possitne esse tanta. Philosophi autem in suis lectulis plerumque moriuntur. Esse enim, nisi eris, non potes.</p>
      <p>Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari. Id Sextilius factum negabat. Quorum sine causa fieri nihil putandum est. Quae autem natura suae primae institutionis oblita est?</p>
    </article>
  </main>

  <aside aria-label="Sidebar">
    <section>
      <h2>Share</h2>
      <ul>
        <li><a href="#">Facebook</a></li>
        <li><a href="#">Twitter</a></li>
        <li><a href="#">Email</a></li>
      </ul>
    </section>
    <section>
      <h2>Recommended</h2>
      <ul>
        <li>
          <article>
            <h3><a href="#">Related article</a></h3>
            <p>Article description</p>
          </article>
        </li>
        <li>
          <article>
            <h3><a href="#">Related article</a></h3>
            <p>Article description</p>
          </article>
        </li>
      </ul>
    </section>
  </aside>

  <footer>
    <ul>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
    </ul>
  </footer>
</body>

如果你想玩玩它,这里是在 CodePen 中的布局(抱歉移动用户,它不适合移动设备)

使用aria-labelledby

但假设你有一个庞大的国际观众,他们说各种各样的语言。在这种情况下,最好使用aria-labelledby属性。这就是它看起来的样子。

<body>

  <header>
    <a href="/" title="Go to home page">
      <img src="logo.png" alt="Site logo">
    </a>
    <nav aria-labelledby="primary-nav-label">
      <div id="primary-nav-label" hidden>Primary</div>
      <ul>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
        <li><a href="#">Primary nav</a></li>
      </ul>
    </nav>
    <form role="search" aria-labelledby="search-label">
      <div id="search-label" hidden>Site</div>
      <label>
        <span>Search</span>
        <input type="search"/>
      </label>
      <button type="submit">Submit</button>
    </form>
  </header>

  <nav aria-labelledby="secondary-nav-label">
    <div id="secondary-nav-label" hidden>Secondary</div>
    <ul>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
      <li><a href="#">Secondary nav</a></li>
    </ul>
  </nav>

  <main>
    <article>
      <h1>Main article heading</h1>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Nihil acciderat ei, quod nollet, nisi quod anulum, quo delectabatur, in mari abiecerat. Unum est sine dolore esse, alterum cum voluptate. Laboro autem non sine causa; Theophrasti igitur, inquit, tibi liber ille placet de beata vita? Nihil opus est exemplis hoc facere longius. Duo Reges constructio interrete. Graecum enim hunc versum nostis omnes Suavis laborum est praeteritorum memoria. Haec et tu ita posuisti, et verba vestra sunt.</p>

      <h2>Article secondary heading</h2>
      <p>Nos commodius agimus. A mene tu? Tantum dico, magis fuisse vestrum agere Epicuri diem natalem, quam illius testamento cavere ut ageretur. Tenesne igitur, inquam, Hieronymus Rhodius quid dicat esse summum bonum, quo putet omnia referri oportere? Nihilo beatiorem esse Metellum quam Regulum. Sed quanta sit alias, nunc tantum possitne esse tanta. Philosophi autem in suis lectulis plerumque moriuntur. Esse enim, nisi eris, non potes.</p>
      <p>Sunt enim quasi prima elementa naturae, quibus ubertas orationis adhiberi vix potest, nec equidem eam cogito consectari. Id Sextilius factum negabat. Quorum sine causa fieri nihil putandum est. Quae autem natura suae primae institutionis oblita est?</p>
    </article>
  </main>

  <aside aria-labelledby="sidebar-label">
    <div id="sidebar-label" hidden>Sidebar</div>
    <section>
      <h2>Share</h2>
      <ul>
        <li><a href="#">Facebook</a></li>
        <li><a href="#">Twitter</a></li>
        <li><a href="#">Email</a></li>
      </ul>
    </section>
    <section>
      <h2>Recommended</h2>
      <ul>
        <li>
          <article>
            <h3><a href="#">Related article</a></h3>
            <p>Article description</p>
          </article>
        </li>
        <li>
          <article>
            <h3><a href="#">Related article</a></h3>
            <p>Article description</p>
          </article>
        </li>
      </ul>
    </section>
  </aside>

  <footer>
    <ul>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
      <li><a href="#">Footer link</a></li>
    </ul>
  </footer>
</body>

使用 aria 的结果

此时,网站的标题结构看起来像这样。

  • <h1> 主文章标题
    • <h2> 文章次要标题
    • <h2> 分享
    • <h2> 推荐
      • <h3> 相关文章
      • <h3> 相关文章

文档大纲(假设原始大纲算法已实现)看起来像这样。

  • <body> 文档
    • <nav> 主要
    • <nav> 次要
    • <article> 主文章标题
      • <section (implied)> 文章次要标题
    • <aside> 侧边栏
      • <section> 分享
      • <section> 推荐
        • <article> 相关文章
        • <article> 相关文章

你可能会认为文档大纲看起来有点简陋。像标题和页脚以及搜索之类的内容不应该在里面公布吗?请记住,这只是显式的内容。通过在良好的结构中使用正确的 HTML 元素,我们可以免费获得很多隐式信息提供给用户。这是一个简化的版本,说明屏幕阅读器用户如何体验网站。

  • [<title> 元素中使用的文本]
    • 横幅地标
      • 链接,网站徽标 [(聚焦时)“转到主页”]
      • “主要”导航地标
        • [导航链接列表]
      • “网站”搜索地标
    • “次要”导航地标
      • [导航链接列表]
    • 主要地标
      • “主文章标题”文章地标,标题级别 1
        • [内容]
        • 标题级别 2,“文章次要标题”
          • [内容]
    • “侧边栏”补充地标
      • “分享”区域地标,标题级别 2
        • [分享链接列表]
      • “推荐”区域地标,标题级别 2
        • 包含 2 个项目的列表
          • 项目,“相关文章”文章地标,标题级别 3
            • [内容]
          • 项目,“相关文章”文章地标,标题级别 3
            • [内容]
    • 内容信息地标
      • [页脚链接列表]

如你所见,当你考虑到从使用良好的 HTML 结构获得的所有额外隐式信息时,网站结构对屏幕阅读器用户变得非常清晰易懂。

因此,即使没有浏览器支持文档大纲算法,它仍然值得付出一些努力来思考大纲。屏幕阅读器仍然会告诉用户某件事是什么类型的部分,部分从哪里开始和(有时)在哪里结束(取决于屏幕阅读器),以及部分标签是什么。这意味着你为创建良好文档结构所付出的努力不会白费。

这种类型的结构有很多好处。

  • 该页面与文档大纲算法完全兼容,防范了该算法在未来在真实浏览器中实现时的风险。
  • 标题结构完全符合逻辑。
  • 通过标题导航的屏幕阅读器用户可以快速跳转到重要信息。
  • 通过地标导航的屏幕阅读器用户有很多有用的地标来移动页面。
  • 屏幕阅读器用户能够快速了解每个部分包含什么,而无需阅读其中的任何内容。
  • 内容被分组到语义部分,因此屏幕阅读器用户在离开一个部分并进入另一个部分时不会感到困惑。
  • 搜索引擎能够更好地理解每个部分包含的信息,这可能会提高 SEO。
  • 有视力的用户可以利用本机浏览器功能,例如阅读器模式。

当你需要<h7>时会发生什么?

在标记分节元素时,还有一个我还没有解决的症结。假设你设法用完了所有六个本机标题级别,现在卡住了,还需要一个。该怎么办?

如果只是为了标记一个部分,你可以使用aria-labelledby技术。假设你真的希望这个标题出现在标题结构中,或者你只是想尽可能避免使用 ID。无论出于何种原因,你都需要一个<h7>元素,但<h7>不存在。

此时,aria-level属性就派上用场了。aria-level属性将定义对role="heading"元素应用的标题级别应是什么。这就是W3C 推荐创建<h7>元素的方式。

<div role="heading" aria-level="7">This is a valid heading level 7 element</div>

并非所有屏幕阅读器都支持此语法。我知道 JAWS 将这些元素视为<h2>元素而不是<h7>元素。如果你知道有任何屏幕阅读器不支持此功能,请向屏幕阅读器开发人员报告错误,并在下面留言。

当我需要使用<h7>时,我通常会使用<h6>元素的隐式role="heading"aria-level属性将覆盖<h6>元素的隐式“6”级别。但这没有得到 W3C 的明确认可。它更简洁,并且允许标题仍然出现在文档大纲和标题结构测试工具中(尽管它们通常会显示为<h6><h2>级别标题,而不是<h7>级别标题)。

<h6 aria-level="7">This is also a valid heading level 7 element</h6>

通过使用aria-level,你现在可以访问无限数量的标题级别!

你的网站结构良好吗?

既然你知道了如何进行正确的 HTML 结构,你能将你学到的东西应用到你的网站吗?

我发现了一个非常好的浏览器扩展名为“Headings Map”,它可以在ChromeFirefox中使用。此扩展程序将允许你轻松查看网站的扁平标题结构表示(即所有浏览器当前如何读取标题结构)以及文档结构在支持文档大纲算法的浏览器中的样子(即支持大纲算法的理论未来浏览器将如何呈现网站结构)。HTML5 大纲视图需要在设置菜单中先启用。这是为了防止用户被误认为能够在生产网站中使用大纲算法。

The Headings Map extension showing the flat heading structure result on the left and the result of the Document Outline Algorithm on the right.

Headings Map 目前不支持 HTML5 大纲选项卡中分节元素上的aria-labelaria-labelledby属性。我一直在与开发人员交谈,他正在努力解决这个问题。如果你知道任何已经考虑了aria-labelaria-labelledby的文档大纲测试工具,请在评论中分享其链接。

获得良好的文档结构测试工具后,请检查标题结构和文档大纲是否显示了逻辑顺序,没有任何缺失的标题或缺失的部分标签。

下载并使用屏幕阅读器

测试从使用正确 HTML 获得的隐式语义的最佳方法是下载一个实际的屏幕阅读器,并尝试使用它来浏览你的网站。NVDA是真实屏幕阅读器用户使用最广泛的屏幕阅读器之一。它也是**免费的**!

请注意,NVDA 的默认设置针对盲人用户使用进行了优化。这些默认设置可能会让有视力的人感到抓狂。要享受使用 NVDA 的时间,请执行以下步骤(步骤基于 Windows 设置,我没有 Mac)

  1. 下载 NVDA 并安装它
  2. 在您的任务栏中创建一个 NVDA 快捷方式(您将在测试时经常打开和关闭它)
  3. 从任务栏打开 NVDA
  4. 在您的系统托盘中找到 NVDA(见下文)
  5. 右键单击托盘图标 > “首选项” > “设置”
  6. 在左侧面板中选择“鼠标”
  7. 取消选中“启用鼠标跟踪”(您现在可以移动鼠标,而不会让 NVDA 对您大喊大叫)
  8. 按“确定”
  9. 右键单击托盘图标 > “工具” > “语音查看器”(您现在可以看到 NVDA 所说的一切的日志,但请不要在测试时完全依赖它)
  10. 在语音查看器中,选中“启动时显示语音查看器”复选框(它将在您打开 NVDA 时打开语音查看器)
  11. 熟悉一些键盘控制
  12. 要关闭 NVDA,请右键单击托盘图标 > “退出” > “确定”
Windows 上 NVDA 的位置

NVDA 目前不支持<article><section> 元素。有一个 GitHub 上的问题 关于支持<article> 元素。当我开始写这篇文章时,<section> 元素已经被支持了。出于某种原因,对<section> 的支持似乎消失了。这意味着 NVDA 应该被修复。但这并不意味着您应该停止在 HTML 中使用正确的语义。

在构建您的网站时要牢记文档大纲,然后使用标题地图和 NVDA(或其他屏幕阅读器)测试语义。如果您这样做,您将让您的屏幕阅读器用户非常高兴。您甚至可能会让搜索引擎也更高兴。😊


特别感谢 Kevin Galvin(me 2 accessibility 的首席顾问)为围绕在页面顶部使用视觉隐藏的<h1> 元素的可使用性问题提供建议,并建议使用aria-label 作为使用视觉隐藏标题的替代方案。