整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

停止滥用div! HTML语义化介绍

停止滥用div! HTML语义化介绍

文同步本人掘金平台的原创翻译:https://juejin.cn/post/6844903817968893960

DIVS该停止(滥用)了

我们喜欢(使用)<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>
复制代码

Hoo,那有很多的div标签。但是,它有效。我的意思主要是,它具有你需要的结构。并且,我确定在你完成样式添加之后,它看起来会像你想要的那个样子。然而,它有些严重的问题:

  • 可访问性 - 许多a11y tools非常智能,会尽力解析页面结构,以帮助用户按照页面制作者的意图来引导用户,并为用户提供简单的跳转链接来指引他们到自己关心的页面部分。然而,<div>标签并没有真正传递有关文档结构的任何有用信息。世界上最聪明的a11y tool仍然不是人类,不能指望其解析class和id属性,或能够识别全世界开发人员命名块元素的奇怪和狂野的方式。我可以识别到class="article-header-level-2"是一个副标题,但是机器不能。(如果可以的话,把它从我电脑中拿出来,可我也还没准备好进行AGI革命呢。)
  • 可读性 - 要阅读此代码,你需要仔细扫描类名,从<div class="..."></div>样板之间挑选出来。一旦你(的代码)深入几个层次,跟踪哪个</div>结束标记与哪个<div ...>开始标记对应,那就变得很棘手了。你开始非常依赖IDE功能,例如着色不同的缩进级别或突出显示匹配的标记以跟踪您的位置,而在较长的文档中,它可能需要在这些功能之上进行大量的滚动。
  • 一致性和标准 - 开始新的工作或转移到新项目,并且必须从头学习代码库中使用的让人抓狂的标记,那可能会令人很沮丧。如果每个人都有标准化的方法来标记web文档中常见结构,那么在不熟悉代码库的情况下,都可以很容易的浏览HTML文件并快速处理它应该展示的内容。如果只有一个这样的标准...

HTML5: 这个标准

HTML5并不新奇。这是轻描淡写;最初的工作草稿于2008年1月(11年前)发布,以征求公众意见,并于4年半前,2014年10月份成为一个全面W3C的推荐。所以,就像它已经存在了一段时间。

HTML5的主要进步之一是引入了一组标准化的语义元素。术语“语义”指的是单词或事物的含义,因此”语义元素“是用于以更有意义的方式标记文档结构的元素,这种方式可以清楚地表明它们的用途和它们在文件中服务的目的是什么。而且重要的是,由于它们是标准化的,定义文档的这些元素可以被每个人使用并理解,包括机器人。

我认为HTML5规范本身在<div>元素定义下的一个注释中很好地总结了这个问题:

注释: 强烈建议作者将div元素视为最后采取的元素,在没有其它元素适合的(情况下)。使用更合适的元素而不是div元素可以使读者更容易访问,并且更容易为作者提供可维护性。-- www.w3.org/TR/html5/gr…

我将语义块元素分为两类:主要结构内容指标。这些不是标准的条款或者其它条款;我在这篇文章中做了一些(区分)。但我认为这种区分足够有用。?♂?

主要结构

有一个超级常见的模式,可在互联网上的网站,教程甚至CSS库中找到,并且有充分的理由。我们经常将最顶层的页面划分为三个区域:页眉主页页脚,然后根据需要将这些区域划分为多个区域。我在上面的例子中包含了这个来证明这点:

<div class="container" id="header">...</div>
<div class="container" id="main">
    ...
    <div class="article-section">...</div>
    ...
</div>
<div class="container" id="footer">...</div>
复制代码

我已经看过(并且使用过)这种模式很久了,以这种方式构造文档非常有意义,既可以读取HTML,又可以更加简单地在CSS中设置页面样式。页眉和页脚元素页可以使用PHP或Rails/ERB等语言中的部分模版来更易于使用,因为你可以在整个站点中包含常见的页眉和页脚部分:

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

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

<?php include 'header.php'; ?>
复制代码

所以这就是事情:每个人都认为这是一个很好的模式。这包括WHATWG和W3C的人员,他们将模式标准化为HTML5中的四个新元素,名称非常清晰:<header>, <main>, <footer>和<section>。

Bookends: <header> 和 <footer>

<header> 和 <footer>元素基本上是双胞胎:它们在规范中的定义非常相似,并遵循相同的规则,关于它们被允许使用的位置,唯一区别在于它们的语义目的:页眉在事物的前面,页脚在事物的末尾。对于事物,我的意思不仅仅是页面的: 这对元素的设计用于文档的任何部分,代表一大块内容,具有明确的开头和结尾。这可以包括表格,文章,文章部分,社交媒体网站上的帖子,卡片等。

页眉和页脚在语义上接近sectioning root或sectioning content元素。像<body>, <blockquote>, <section>, <td>,<aside>等许多其它元素;如果你想了解完整的列表,就点击上面的链接。辅助技术可以使用这些元素和其它元素生成文档大纲,这可以帮助用户更轻松的访问它。在每个sectioning root/content中,你不应该使用超过一个的<header>或<footer>。(一个就好,不能两个相同)

作为最后说明,<header>经常作为其上下文保存标题元素(<h1>-<h6>)。这不是必须的,但可以帮助将其它相关元素与标题分组,比如链接,图片或子标题,并且可以维持一直的结构,即使标题是<header>中的唯一元素。

好东西:<main>

第三个主要区域元素--<main>很特别。规范中说明了关于<main>的两个非常重要的内容:

文档的主要内容区域包括文档的特定内容,且不包括在一组文档中重复的内容,例如站点导航链接,版本信息,站点的徽标,横幅和搜索表单(除非文档或应用的主功能是一种搜索形式)-- www.w3.org/TR/html5/gr…

所以,<main>是你放置好东西的区域,是页面的重要部分,特别是用户访问此页面的原因(或说目的),而不是您的站点。换句话来说,主要内容。

所有其它东西,徽标、搜索表单和导航栏等都可以在<body>中的<header>或<footer>中,但是在<main>之外。

文档中不能有多个可见的main元素。如果文档中存在多个main元素,则必须使用隐藏属性隐藏所有其它(main)实例。 -- www.w3.org/TR/html5/gr…

这很独特。和<header>和<footer>(以及其它块元素不同),<main>不能在任意切片内容的整个页面中使用;它应该只被使用一次。或者更确切地说,它可以在文档中多次被使用,但是一次只能看到一个<main>元素,所有其它的(

)必须被使用隐藏属性隐藏,如CSS中的display:none。如果您思考下,(你会明白)这在应用程序中预加载视图是种很有用的模式:创建一个新的<mian hidden>,获取用户可能接下来查看的一些内容(例如:系列文中的下一篇,下一张幻灯图放映等),然后,当用户点击链接/按钮加载该视图时,通过在两者上切换隐藏属性,将当前的<main>切换到预加载的(那个)。

在继续之前,我们暂停下并查看上面的示例。如果我们使用<header>,<main>和<footer>作为文章的主要结构,它的外观如下:

<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>派上用场了。这是在系列规则中最简单的一个:从结构上讲,它基本上只是一个具有特殊含义的<div>。一个<section>开始一个新的"sectioning content"区域,因此它可以有自己的<header>和<footer>。

那么,<section>和普通的旧<div>之间有什么区别,然后,你应该在什么时候使用它们呢?好吧,允许我再次引用规范:

笔记:

元素不是通用容器元素。当一个元素仅是用于样式目的或为脚本编写提供便利的时候,鼓励作者使用[div](https://www.w3.org/TR/html5/grouping-content.html#elementdef-div)元素。一般规则是元素仅在元素内容在文本[大纲](https://www.w3.org/TR/html5/sections.html#outline)中明确列出时候才适用。-- [https://www.w3.org/TR/html5/sections.html#the-section-element](https://www.w3.org/TR/html5/sections.html#the-section-element)

你知道,概述来说,HTML5规范实际上是可读的。它是那个比较可读的规范之一。每当我浏览它以获取快速答复时,我都不可避免地学到一些意想不到的和有用的东西,尤其是当我开始点击链接的时候。有时(你也)试试吧!

简而言之,如果要在目录中列出文档的一部分,请使用<section>。如果没有,请使用<div>或其它元素。

内容指标

很好,我们已经得到了一个坚固的页面结构。我们已经明确标记了页面的主要内容区域,而不仅仅是单独调整<div>,我们已经调整出了页眉,页脚和章节。但是,肯定还有比我们的文档更多的语义。

让我们来谈谈HTML5中添加的一些元素,它们传达的内容语义而不是结构。

整体:<article>

<article>元素用于表示完全独立的内容区域,这些内容可以从页面中提取出来并放入另一个内容中,并且仍然有意义。这可能是文字文章或博客,但也可用于社交媒体帖子,如推特或脸书的墙贴。

HTML5规范建议文章总有一个标题,标识它是什么,理想的情况下使用标题元素(<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>旨在清楚地识别页面上的主要导航块,帮助用户围绕站点其余部分找到路径的链接组(例如站点地图或标题中的链接列表)或当前页面(例如目录)。

在我们的示例顶部,让我们将<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的更多信息,请点击以下链接:

  • The W3C's RDFa primer
  • A description of schemas and links to a bunch of them on schema.org
  • The LocalBusiness schema used above

总结

好了,我们已经介绍了很多,我们已经看到很多零零散散的元素应用到我们的例子中。那么,让我们把它们放在一起看看它的样子。

<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倍,而且对于搜索引擎优化和可访问性目的而言,其效率将提高100倍。

这些绝不是HTML中唯一的语义元素。有很多其它元素可以帮助你标记和构建你的文本内容,嵌入媒体资源等等。如果你喜欢这个并且希望深入挖掘,这里有一些(标签)可以查看下。你可能认识一些:

  • <aside>
  • <blockquote>
  • <cite>
  • code
  • <data>
  • <del>
  • <figure>
  • <ins>
  • <time>
  • <var>

这只是一个开始!就像我说的,当你开始阅读HTML规范时,很难停下来。它是种非常丰富的语言,我认为人们经常会低估这种语言。

前在项目的过程中遇到了一个问题,某个 div 希望始终显示在最上面,而在之后的元素都显示在它之下,当时设置了 z-index 也没有效果,不知道什么原因,因此找了一下 CSS 相关资料,解决了这个问题的同时,也学习了很多知识,特此和大家分享一下。

屏幕是一个二维平面,然而 HTML 元素却是排列在三维坐标系中, x 为水平方向, y 为垂直方向, z为屏幕由内向外方向,我们在看屏幕的时候是沿着 z 轴方向从外向内的。由此,元素在用户视角就形成了层叠的关系,某个元素可能覆盖了其他元素也可能被其他元素覆盖;

这里有几个重要的概念:层叠上下文 (堆叠上下文, Stacking Context)、层叠等级 (层叠水平, Stacking Level)、层叠顺序 (层叠次序, 堆叠顺序, Stacking Order)、z-indexBFC(块级格式化上下文,Block Formatting Context),这些概念共同决定了你看到元素的位置,下面我们就围绕着这几个概念来一起学习一下。

声明:

  1. 以下定位元素指的是 position:absolute|fixed|relative|sticky
  2. 以下非定位元素指的是 position:initial|static
  3. 本文蛮长的,但是如果你可以仔细看完,那对相关概念的掌握就非常好了 (~o ̄▽ ̄)~

1. 层叠上下文 (Stacking Context)

层叠上下文 (堆叠上下文, Stacking Context),是 HTML 中一个三维的概念。在 CSS2.1 规范中,每个元素的位置是三维的,当元素发生层叠,这时它可能覆盖了其他元素或者被其他元素覆盖;排在 z 轴越靠上的位置,距离屏幕观察者越近。

文章 <关于z-index 那些你不知道的事> 有一个很好的比喻,这里引用一下;

可以想象一张桌子,上面有一堆物品,这张桌子就代表着一个层叠上下文。如果在第一张桌子旁还有第二张桌子,那第二张桌子就代表着另一个层叠上下文。现在想象在第一张桌子上有四个小方块,他们都直接放在桌子上。在这四个小方块之上有一片玻璃,而在玻璃片上有一盘水果。这些方块、玻璃片、水果盘,各自都代表着层叠上下文中一个不同的层叠层,而这个层叠上下文就是桌子。

每一个网页都像一个房间,这个房间就是 <html></html>,其他层叠上下文就像这个房间里的桌子,HTML 标签中的一切都被置于这个房间中。

当给一个元素的 position 值赋为 fixed 或 sticky 值时,你就创建了一个新的层叠上下文,其中有着独立于页面上其他层叠上下文和层叠层的层叠层,这就相当于你把另一张桌子带到了房间里。

层叠上下文 1 (Stacking Context 1)是由文档根元素形成的, 层叠上下文 2 和 3 (Stacking Context 2, 3) 都是层叠上下文 1 (Stacking Context 1) 上的层叠层。他们各自也都形成了新的层叠上下文,其中包含着新的层叠上下文。

在层叠上下文中,其子元素按照上面解释的规则进行层叠。形成层叠上下文的方法有:

  • 根元素 <html></html>;
  • position 值为 absolute|relative,且 z-index 值不为 auto;
  • position 值为 fixed|sticky;
  • z-index 值不为 auto 的 flex 元素,即父元素属性 display:flex|inline-flex;
  • opacity 属性值小于 1 的元素;
  • transform 属性值不为 none 的元素;
  • mix-blend-mode 属性值不为 normal 的元素;
  • filter、 perspective、 clip-path、 mask、 mask-image、 mask-border、 motion-path值不为 none 的元素;
  • perspective 值不为 none 的元素;
  • isolation 属性被设置为 isolate 的元素;
  • will-change 中指定了任意 CSS 属性,即便你没有直接指定这些属性的值
  • -webkit-overflow-scrolling 属性设置为 touch 的元素;

总结:

  1. 层叠上下文可以包含在其他层叠上下文中,并且一起组建了一个有层级的层叠上下文;
  2. 每个层叠上下文完全独立于它的兄弟元素,当处理层叠时只考虑子元素,类似于 BFC;
  3. 每个层叠上下文是自包含的:当元素的内容发生层叠后,整个该元素将会在父级叠上下文中按顺序进行层叠;

2. 层叠等级 (Stacking Level)

层叠等级 (层叠水平, Stacking Level) 决定了在同一个层叠上下文中,元素在 z 轴上的显示的顺序;

  1. 普通元素的层叠等级优先由其所在的层叠上下文决定;
  2. 层叠等级的比较,只有在同一个层叠上下文元素中才有意义;
  3. 在同一个层叠上下文中,层叠等级描述定义的是该层叠上下文中的元素在 z 轴上的上下顺序;

对于普通元素的层叠水平探讨只局限于在当前层叠上下文中:

层叠上下文本身是一个强力的「层叠结界」,普通的元素水平是无法突破这个结界和结界外的元素去较量层叠水平的。

— CSS 世界

另外,层叠等级并不一定由 z-index 决定,只有定位元素的层叠等级才由 z-index 决定,其他类型元素的层叠等级由层叠顺序、他们在 HTML 中出现的顺序、他们的祖先元素的层叠等级一同决定,详细的规则见下面层叠顺序的介绍。

3. z-index

在 CSS 2.1 中, 所有的盒模型元素都处于三维坐标系中。除了我们常用的横坐标和纵坐标, 盒模型元素还可以沿着「z 轴」层叠摆放,当他们相互覆盖时,z 轴顺序就变得十分重要。

-- CSS 2.1 Section 9.9.1 - Layered presentation

z-index 只适用于定位的元素,对非定位元素无效,它可以被设置为正整数、负整数、 0、 auto,如果一个定位元素没有设置 z-index,那么默认为 auto;

元素的 z-index 值只在同一个层叠上下文中有意义。如果父级层叠上下文的层叠等级低于另一个层叠上下文的,那么它 z-index 设的再高也没用。所以如果你遇到 z-index 值设了很大,但是不起作用的话,就去看看它的父级层叠上下文是否被其他层叠上下文盖住了。

4. 层叠顺序 (Stacking Order)

层叠顺序 (层叠次序, 堆叠顺序, Stacking Order) 描述的是元素在同一个层叠上下文中的顺序规则(之前的层叠上下文和层叠等级是概念),从层叠的底部开始,共有七种层叠顺序:

  1. 背景和边框:形成层叠上下文的元素的背景和边框。
  2. 负 z-index 值:层叠上下文内有着负 z-index 值的定位子元素,负的越大层叠等级越低;
  3. 块级盒:文档流中的块级、非定位子元素;
  4. 浮动盒:非定位浮动元素;
  5. 行内盒:文档流中行内、非定位子元素;
  6. z-index: 0: z-index 为 0 或 auto 的定位元素, 这些元素形成了新的层叠上下文;
  7. 正 z-index 值: z-index 为正的定位元素,正的越大层叠等级越高;

第 7 级顺序的元素会显示在之前顺序元素的上方,也就是看起来覆盖了更低级的元素:

除层叠顺序优先级规则之外,还有一条后来居上规则:同一个层叠顺序的元素按照在 HTML 里出现的顺序依次层叠。这两个规则共同决定浏览器元素在文档中是如何层叠的。

5. 文档流 (Document Flow)

5.1 常规流 (Normal flow)

  • 在常规流中,盒一个接着一个排列;
  • 在块级格式化上下文里面, 它们竖着排列;
  • 在行内格式化上下文里面, 它们横着排列;
  • 当 position 为 static 或 relative,并且 float 为 none 时会触发常规流;
  • 对于静态定位(static positioning), position:static,盒的位置是常规流布局里的位置;
  • 对于相对定位(relative positioning), position:relative,盒偏移位置由 top、 bottom、 left、 right 属性定义。即使有偏移,仍然保留原有的位置,其它常规流不能占用这个位置。

5.2 浮动 (Floats)

  1. 左浮动元素尽量靠左、靠上,右浮动同理;
  2. 这导致常规流环绕在它的周边,除非设置 clear 属性;
  3. 浮动元素不会影响块级元素的布局;
  4. 但浮动元素会影响行内元素的布局,让其围绕在自己周围,撑大父级元素,从而间接影响块级元素布局;
  5. 最高点不会超过当前行的最高点、它前面的浮动元素的最高点;
  6. 不超过它的包含块,除非元素本身已经比包含块更宽;
  7. 行内元素出现在左浮动元素的右边和右浮动元素的左边,左浮动元素的左边和右浮动元素的右边是不会摆放浮动元素的;

5.3 绝对定位 (Absolute positioning)

  1. 绝对定位方案,盒从常规流中被移除,不影响常规流的布局;
  2. 它的定位相对于它的包含块,相关 CSS 属性: top、 bottom、 left、 right;
  3. 如果元素的属性 position 为 absolute 或 fixed,它是绝对定位元素;
  4. 对于 position:absolute,元素定位将相对于上级元素中最近的一个 relative、 fixed、 absolute,如果没有则相对于 body;

6. BFC (Block Formatting Context)

6.1 什么是 BFC

BFC (Block Formatting Context) 块级格式化上下文,是用于布局块级盒子的一块渲染区域,相对应的还有 IFC(Inline Formatting Context)内联格式化上下文,不是本文重点,读者可以自行查阅相关知识。

BFC 是 Web 页面 CSS 视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域。

— MDN - 块格式化上下文

一个 BFC 的范围包含创建该上下文元素的所有子元素,但不包括创建了新 BFC 的子元素的内部元素。这从另一方角度说明,一个元素不能同时存在于两个 BFC 中。因为如果一个元素能够同时处于两个 BFC 中,那么就意味着这个元素能与两个 BFC 中的元素发生作用,就违反了 BFC 的隔离作用。

触发 BFC 的方式有:

  1. 根元素,即 HTML 标签;
  2. 浮动元素,即 float 值为 left、 right;
  3. overflow 值不为 visible,即值为 auto、 scroll、 hidden;
  4. display 值为 inline-block、 table-cell、 table-caption、 table、 inline-table、 flex、 inline-flex、 grid、 inline-grid;
  5. 定位元素: position 值为 absolute、 fixed;
  6. contain 为 layout、 content、 paint 的元素;

注意: display:table 也可以生成 BFC 的原因在于 Table 会默认生成一个匿名的 table-cell,是这个匿名的 table-cell 生成了 BFC。

6.2 用法

1. 阻止相邻元素的 margin 合并

属于同一个 BFC 的两个相邻块级子元素的上下 margin 会发生重叠,(设置 writing-mode:tb-rl时,水平 margin 会发生重叠)。所以当两个相邻块级子元素分属于不同的 BFC 时可以阻止 margin 重叠。可以给任一个相邻块级盒子的外面包一个 div,通过改变此 div 的属性使两个原盒子分属于两个不同的 BFC,以此来阻止 margin 重叠。

代码和预览参见:Codepen - 使用BFC阻止margin合并:https://codepen.io/SHERlocked93/pen/eVOevN

2. 阻止元素被浮动元素覆盖

一个正常文档流的块级元素可能被一个 float 元素覆盖,挤占正常文档流,因此可以设置一个元素的 float、 display、 position 值等方式触发 BFC,以阻止被浮动盒子覆盖。

代码和预览参见:Codepen - 使用BFC阻止元素被浮动元素覆盖:https://codepen.io/SHERlocked93/pen/pazdzB

3. 包含浮动元素

通过改变包含浮动子元素的父盒子的属性值,触发 BFC,以此来包含子元素的浮动盒子。

代码和预览参见:Codepen - 使用BFC包含浮动元素:https://codepen.io/SHERlocked93/pen/OQLOqG

7. 实战

下面一起来看几个例子实战一下,帮助理解。

7.1 普通情况

三个 relative 定位的 div 块中各有 absolute 的不同颜色的 span.red、 span.green、 span.blue,它们都设置了 position:absolute;

代码和预览参见:Codepen - 普通情况:https://codepen.io/SHERlocked93/pen/aaPord

那么当没有元素包含 z-index 属性时,这个例子中的元素按照如下顺序层叠(从底到顶顺序):

  1. 根元素的背景和边界;
  2. 块级非定位元素按 HTML 中的出现顺序层叠;
  3. 行内非定位元素按 HTML 中的出现顺序层叠;
  4. 定位元素按 HTML中的出现顺序层叠;

红绿蓝都属于 z-index 为 auto 的定位元素,因此按照 7 层层叠顺序规则来说同属于层叠顺序第 6 级,所以按 HTML 中的出现顺序层叠:红->绿->蓝

7.2 在相同层叠上下文的父元素内的情况

红绿位于一个 div.first-box 下,蓝位于 div.second-box 下,红绿蓝都设置了 position:absolute, first-box 与 second-box 都设置了 position:relative;

代码和预览参见:Codepen - 父元素不同但都位于根元素下:https://codepen.io/SHERlocked93/pen/RYENBw

这个例子中,红蓝绿元素的父元素 first-box 与 second-box 都没有生成新的层叠上下文,都属于根层叠上下文中的元素,且都是层叠顺序第 6 级,所以按 HTML 中的出现顺序层叠:红->绿->蓝

7.3 给子元素增加 z-index

红绿位于一个 div.first-box 下,蓝黄位于 div.second-box 下,红绿蓝都设置了 position:absolute,如果这时给绿加一个属性 z-index:1,那么此时 .green 位于最上面;

如果再在 .second-box 下 .green 后加一个绝对定位的 span.gold,设置 z-index:-1,那么它将位于红绿蓝的下面;

代码和预览参见:Codepen - 设置了z-index:https://codepen.io/SHERlocked93/pen/gdZOrK

这个例子中,红蓝绿黄元素的父元素中都没有生成新的层叠上下文,都属于根层叠上下文中的元素

  1. 红蓝都没有设置 z-index,同属于层叠顺序中的第 6 级,按 HTML 中的出现顺序层叠;
  2. 绿设置了正的 z-index,属于第 7 级;
  3. 黄设置了负的 z-index,属于第 2 级;

所以这个例子中的从底到高显示的顺序就是:黄->红->蓝->绿

7.4 在不同层叠上下文的父元素内的情况

红绿位于一个 div.first-box 下,蓝位于 div.second-box 下,红绿蓝都设置了 position:absolute,如果 first-box 的 z-index 设置的比 second-box 的大,那么此时无论蓝的 z-index 设置的多大 z-index:999,蓝都位于红绿的下面;如果我们只更改红绿的 z-index 值,由于这两个元素都在父元素 first-box 产生的层叠上下文中,此时谁的 z-index 值大,谁在上面;

代码和预览参见:Codepen - 不同层叠上下文的父元素:https://codepen.io/SHERlocked93/pen/gdZbOJ

这个例子中,红绿蓝都属于设置了 z-index 的定位元素,不过他们的父元素创建了新的层叠上下文;

  1. 红绿的父元素 first-box 是设置了正 z-index 的定位元素,因此创建了一个层叠上下文,属于层叠顺序中的第 7 级;
  2. 蓝的父元素 second-box 也同样创建了一个层叠上下文,属于层叠顺序中的第6级;
  3. 按照层叠顺序, first-box 中所有元素都排在 second-box 上;
  4. 红绿都属于层叠上下文 first-box 中且设置了不同的正 z-index,都属于层叠顺序中第 7 级;
  5. 蓝属于层叠上下文 second-box,且设置了一个很大的正 z-index,属于层叠元素中第 7 级;
  6. 虽然蓝的 z-index 很大,但是因为 second-box 的层叠等级比 first-box 小,因此位于红绿之下;

所以这个例子中从低到到显示的顺序:蓝->红->绿

(我遇到的的情况就属于这个例子类似情形)

7.5 给子元素设置 opacity

红绿位于 div.first-box 下,蓝位于 div.second-box 下,红绿蓝都设置了 position:absolute,绿设置了 z-index:1,那么此时绿位于红蓝的最上面;

如果此时给 first-box 设置 opacity:.99,这时无论红绿的 z-index 设置的多大 z-index:999,蓝都位于红绿的上面;

如果再在 .second-box 下 .green 后加一个 span.gold,设置 z-index:-1,那么它将位于红绿蓝的下面;

代码和预览参见:Codepen - opacity的影响:https://codepen.io/SHERlocked93/pen/GXPRWB

之前已经介绍了,设置 opacity 也可以形成层叠上下文,因此:

  1. first-box 设置了 opacity, first-box 成为了一个新的层叠上下文;
  2. second-box 没有形成新的层叠上下文,因此其中的元素都属于根层叠上下文;
  3. 黄属于层叠顺序中第 2 级,红绿属于第 7 级, first-box 属于第 6 级,蓝属于层叠顺序中第6级且按HTML出现顺序位于 first-box 之上;

所以这个例子中从低到到显示的顺序:黄->红->绿->蓝


关注微信公众号:安徽思恒信息科技有限公司,了解更多技术内容……

据来自MDN整理https://developer.mozilla.org/

文档元数据

元数据(Metadata)含有页面的相关信息,包括样式、脚本及数据,能帮助一些软件(例如 搜索引擎、浏览器 等等)更好地运用和渲染页面。对于样式和脚本的元数据,可以直接在网页里定义,也可以链接到包含相关信息的外部文件

<base>

HTML <base> 元素 指定用于一个文档中包含的所有相对 URL 的根 URL。一份中只能有一个 <base> 元素

<head>

HTML head 元素 规定文档相关的配置信息(元数据),包括文档的标题,引用的文档样式和脚本等。

<link>

HTML外部资源链接元素 (<link>) 规定了当前文档与外部资源的关系。该元素最常用于链接样式表,此外也可以被用来创建站点图标(比如PC端的“favicon”图标和移动设备上用以显示在主屏幕的图标)

<meta>

HTML <meta> 元素表示那些不能由其它HTML元相关元素 (<base>, <link>, <script>, <style> 或 <title>) 之一表示的任何元数据信息

<style>

HTML的<style>元素包含文档的样式信息或者文档的部分内容。默认情况下,该标签的样式信息通常是CSS的格式

<title>

HTML <title> 元素 定义文档的标题,显示在浏览器的标题栏或标签页上。它只可以包含文本,若是包含有标签,则包含的任何标签都不会被解释

内容分区

内容分区元素允许你将文档内容从逻辑上进行组织划分。使用包括页眉(header)、页脚(footer)、导航(nav)和标题(h3~h6)等分区元素,来为页面内容创建明确的大纲,以便区分各个章节的内容。

<address>

HTML <address> 元素 表示其中的 HTML 提供了某个人或某个组织(等等)的联系信息

<article>

HTML <article>元素表示文档、页面、应用或网站中的独立结构,其意在成为可独立分配的或可复用的结构,如在发布中,它可能是论坛帖子、杂志或新闻文章、博客、用户提交的评论、交互式组件,或者其他独立的内容项目

<aside>

HTML <aside> 元素表示一个和其余页面内容几乎无关的部分,被认为是独立于该内容的一部分并且可以被单独的拆分出来而不会使整体受影响

<footer>

HTML <footer> 元素表示最近一个章节内容或者根节点(sectioning root )元素的页脚。一个页脚通常包含该章节作者、版权数据或者与文档相关的链接等信息

<header>

HTML <header> 元素用于展示介绍性内容,通常包含一组介绍性的或是辅助导航的实用元素。它可能包含一些标题元素,但也可能包含其他元素,比如 Logo、搜索框、作者名称,等等

<h1>~<h6>

HTML <h1>–<h6> 标题(Heading)元素呈现了六个不同的级别的标题,<h1> 级别最高,而 <h6> 级别最低

<hgroup>

HTML <hgroup> Element (HTML Headings Group Element) 代表一个段的标题。它规定了在文档轮廓里(the outline of the document )的单一标题是它所属的隐式或显式部分的标题

<main>
HTML <main> 元素呈现了文档的 <body> 或应用的主体部分。主体部分由与文档直接相关,或者扩展于文档的中心主题、应用的主要功能部分的内容组成

<nav>

HTML <nav>元素表示页面的一部分,其目的是在当前文档或其他文档中提供导航链接。导航部分的常见示例是菜单,目录和索引

<section>

HTML <section>元素表示一个包含在HTML文档中的独立部分,它没有更具体的语义元素来表示,一般来说会有包含一个标题

文本内容

使用 HTML 文本内容元素来组织在开标签 <body> 和闭标签 </body> 里的块或章节的内容。这些元素能标识内容的宗旨或结构,而这对于 accessibility SEO 很重要。

<blockquote>
HTML <blockquote> 元素(或者 HTML 块级引用元素),代表其中的文字是引用内容。通常在渲染时,这部分的内容会有一定的缩进(注 中说明了如何更改)。若引文来源于网络,则可以将原内容的出处 URL 地址设置到 cite 特性上,若要以文本的形式告知读者引文的出处时,可以通过 <cite> 元素

<dd>

HTML <dd> 元素(HTML 描述元素)用来指明一个描述列表 (<dl>) 元素中一个术语的描述。这个元素只能作为描述列表元素的子元素出现,并且必须跟着一个 <dt> 元素

<div>

HTML <div> 元素 (或 HTML 文档分区元素) 是一个通用型的流内容容器

<dl>
HTML <dl> 元素 (或 HTML 描述列表元素)是一个包含术语定义以及描述的列表,通常用于展示词汇表或者元数据 (键-值对列表)

<dt>

HTML <dt> 元素 (或 HTML 术语定义元素)用于在一个定义列表中声明一个术语。该元素仅能作为 <dl> 的子元素出现。通常在该元素后面会跟着 <dd> 元素, 然而,多个连续出现的 <dt> 元素都将由出现在它们后面的第一个 <dd> 元素定义

<figcaption>
HTML <figcaption> 元素 是与其相关联的图片的说明/标题,用?于描述其父节点 <figure> 元素里的其他数据。这意味着 <figcaption> 在<figure> 块里是第一个或最后一个。同时 HTML Figcaption 元素是可选的;如果没有该元素,这个父节点的图片只是会没有说明/标题

<figure>

HTML <figure> 元素代表一段独立的内容, 经常与说明(caption) <figcaption> 配合使用, 并且作为一个独立的引用单元。当它属于主内容流(main flow)时,它的位置独立于主体。这个标签经常是在主文中引用的图片,插图,表格,代码段等等,当这部分转移到附录中或者其他页面时不会影响到主体

<hr>

HTML <hr> 元素表示段落级元素之间的主题转换(例如,一个故事中的场景的改变,或一个章节的主题的改变)。在HTML的早期版本中,它是一个水平线。现在它仍能在可视化浏览器中表现为水平线,但目前被定义为语义上的,而不是表现层面上

<li>

HTML<li> 元素 (或称 HTML 列表条目元素) 用于表示列表里的条目。它必须包含在一个父元素里:一个有序列表(<ol>),一个无序列表(<ul>),或者一个菜单 (<menu>)。在菜单或者无序列表里,列表条目通常用点排列显示;在有序列表里,列表条目通常在左边显示按升序排列的计数,例如数字或者字母

<main>

HTML <main> 元素呈现了文档的 <body> 或应用的主体部分。主体部分由与文档直接相关,或者扩展于文档的中心主题、应用的主要功能部分的内容组成

<ol>

HTML <ol> 元素 表示多个有序列表项,通常渲染为有带编号的列表

<p>

<p>元素(或者说 HTML 段落元素)表示文本的一个段落。该元素通常表现为一整块与相邻文本分离的文本,或以垂直的空白隔离或以首行缩进。另外,<p> 是块级元素

<pre>

HTML <pre> 元素表示预定义格式文本。在该元素中的文本通常按照原文件中的编排,以等宽字体的形式展现出来,文本中的空白符(比如空格和换行符)都会显示出来。(紧跟在 <pre> 开始标签后的换行符也会被省略)

<ul>
HTML <ul> 元素(或称 HTML 无序列表元素)表示一个内可含多个元素的无序列表或项目符号列表

内联文本语义

使用 HTML 内联文本语义(Inline text semantics)定义一个单词、一行内容,或任意文字的语义、结构或样式。

<a><abbr><b><bdi><bdo><br><cite><code><data><dfn><em><i><kbd><mark><q><rb><rp><rt><rtc><ruby><s><samp><small><span><strong><sub><sup><time><tt><u><var><wbr>

图片和多媒体

HTML 支持各种多媒体资源,例如图像、音频和视频。

<area><audio><img><map><track><video>

内嵌内容

除了常规的多媒体内容,HTML 可以包括各种其他的内容,即使它并不容易交互。

<applet><embed><iframe><noembed><object><param><picture><source>

脚本

为了创建动态内容和 Web 应用程序,HTML 支持使用脚本语言,最突出的就是 JavaScript。某些元素用于支持此功能。

<canvas><noscript><script>

编辑标识

这些元素能标示出某个文本被更改过的部分。

<del><ins>

表格内容

这里的元素用于创建和处理表格数据。

<caption><col><colgroup><table><tabody><td><tfoot><th><thead><tr>

表单

HTML 提供了许多可以起使用的元素,这些元素能用来创建一个用户可以填写并提交到网站或应用程序的表单。

<button><datalist><fiedset><form><input><label><legand><meter><optgroup><option><output><progress><select><textarea>

交互元素

HTML 提供了一系列有助于创建交互式用户界面对象的元素。

<datails><dialog><menu><menuitem><summary>

过时的和弃用的元素

警告:下面这些旧的 HTML 元素已被弃用,且不应再被使用。千万不要在新的项目中使用它们,并且要尽快替换旧项目中的残余。在此列出,仅供参考

<acronym><applet><basefont><bgsound><big><bink><center><command><content><dir><element><font><frame><frameset><image><isindex><keygen><listing><marquee><menuitem><multicol><nextid><nodr><noembed><noframes><plaintext><shadow><spacer><strke><tt><xmp>