【问题标题】:Why do the :before and :after pseudo-elements require a 'content' property?为什么 :before 和 :after 伪元素需要 'content' 属性?
【发布时间】:2013-06-08 16:59:50
【问题描述】:

鉴于以下场景,为什么:after 选择器需要内容属性才能起作用?

.test {
    width: 20px;
    height: 20px;
    background: blue;
    position:relative;
}
			
.test:after {
    width: 20px;
    height: 20px;
    background: red;
    display: block;
    position: absolute;
    top: 0px;
    left: 20px;
}
<div class="test"></div>

请注意,在您指定 content 属性之前,您是看不到伪元素的:

.test {
    width: 20px;
    height: 20px;
    background: blue;
    position:relative;
}
			
.test:after {
    width: 20px;
    height: 20px;
    background: red;
    display: block;
    position: absolute;
    top: 0px;
    left: 20px;
    content:"hi";
}
<div class="test"></div>

为什么这是预期的功能?您会认为显示块会强制元素显示。奇怪的是,您实际上可以在 Web 调试器中看到样式;但是,它们不会显示在页面上。

【问题讨论】:

    标签: css pseudo-element css-content


    【解决方案1】:

    2020 年编辑

    :before

    是 CSS2 的语法以获得更好的实践,你应该编写来自 CSS3 的最新语法

    ::before

    双分号

    完整的答案和差异可以在这里找到: What is the difference between :before and ::before?

    【讨论】:

      【解决方案2】:

      以下是对各种 W3C 规范和草案的一些参考:

      Selectors Level 3

      :before:after 伪元素可用于在元素内容之前或之后插入生成的内容。

      The :before and :after pseudo-elements

      作者使用:before:after 伪元素指定生成内容的样式和位置。正如它们的名字所示,:before:after 伪元素指定了元素的文档树内容之前和之后的内容位置。 content 属性与这些伪元素一起指定插入的内容。

      The content attribute

      首字母:

      此属性与:before:after 伪元素一起用于在文档中生成内容。值的含义如下:

      none - 不生成伪元素。


      应用于::before::after 伪元素的样式会影响生成内容的显示。 content 属性这个生成的内容,如果没有它,默认值content: none 被假定,这意味着没有任何样式可以应用。

      如果您不想多次重复 content:'';,您可以简单地通过在 CSS (JSFiddle example) 中对所有 ::before::after 伪元素进行全局样式化来覆盖它:

      ::before, ::after {
          content:'';
      }
      

      【讨论】:

      • 感谢您的回复。这更符合我正在寻找的内容。但是,谁能想到使用 content: none over display: none 的用例?这个属性的规范似乎很奇怪。
      • display:none 可用于这些伪元素以隐藏生成的内容而不覆盖它。例如:counters 只有在元素悬停时才可见,不必每次都重新生成。这是一个更新的 Fiddle(将鼠标悬停在文本上):jsfiddle.net/VzZUz/1
      • display: none on ::before 和 ::after 与 content: none 在任何情况下都具有完全相同的效果,包括计数器。如果您更愿意使用 display 属性来隐藏伪元素,您可以这样做。如果特异性是一个问题,切换到其他属性也很有用。
      • 我不建议全局启用所有带有不合格选择器的 ::before/::after 伪元素。如果您有非常复杂的页面,这将导致浏览器可能 三倍 渲染框的数量 - 绝大多数生成的内容框从未使用过。 content 的初始值为 none 而不是空字符串是有原因的...
      • ...这可能是戴夫一直在寻找的答案。请参阅 SLaks 答案下的 cmets。你想添加到你的答案还是我应该发布一个单独的充实答案(注意我说“有一个原因”没有详细说明)?
      【解决方案3】:

      您需要为每个::before 和/或::after 伪元素声明content: '' 的原因是因为content 的初始值是normal,它在@987654328 上计算为none @ 和 ::after 伪元素。请参阅spec

      content 的初始值不是空字符串,而是计算为 none::before::after 伪元素的值的原因有两个:

      1. 在每个元素的开头和结尾都有空的内联内容是相当愚蠢的。请记住,::before::after 伪元素的最初目的是在原始元素的主要内容之前和之后插入生成的内容。当没有要插入的内容时,创建一个额外的框只是为了不插入任何内容是没有意义的。所以none 值是为了告诉浏览器不要费心创建额外的框。

        使用空的::before::after 伪元素来创建额外的框以仅用于布局美学的做法相对较新,一些纯粹主义者甚至可能因此将其称为hack .

      2. 在每个元素的开头和结尾都有空的内联内容意味着每个(非替换)元素——包括htmlbody——默认情况下不会生成一个框,而是最多生成三个 /em> 个盒子(以及 more,如果元素已经生成的不仅仅是主盒子,比如具有列表样式的元素)。您将实际使用每个元素的两个额外框中的多少?这可能会使布局成本三倍而收益甚微。

        实际上,即使在这十年中,页面上也只有不到 10% 的元素需要 ::before::after 伪元素进行布局。

      因此,这些伪元素被选择加入——因为让它们选择退出不仅浪费系统资源,而且考虑到它们的原始目的,完全不合逻辑。性能原因也是我建议使用::before, ::after为每个元素生成伪元素的原因。

      但是你可能会问:为什么不在::before, ::after 上将display 属性默认为none?简单:因为display的初始值不是none;它是inline。让inline::before, ::after 上计算到none 不是一种选择,因为那样你就永远无法内联显示它们。将display 的初始值设为none on ::before, ::after 不是一种选择,因为属性只能有一个初始值。 (这就是为什么content 的初始值总是normal 并且它被简单地定义为在::before, ::after 上计算为none。)

      【讨论】:

      • 其他答案都不错,但这应该是公认的答案。它完全回答了这个问题。
      • 下一个问题是:既然content属性只适用于::before::after伪元素,那么为什么它的初始值是normal,它会自动计算到none?为什么不去掉多余的步骤,只做初始值none
      • @Michael_B:我猜他们计划在最终将其推迟到 CSS3 之前使内容与真实元素一起工作(propdef 说“在元素上,总是计算为'正常'。”即使属性没有'不适用于开始的元素)。将初始值设置为 none 意味着整个页面将消失...直到您添加 html, body, body * { content: normal } 所以,为了允许 CSS3 的定义,初始值更有意义,它是正常的,然后对于 ::before 计算为 none,: :after 因为“通常”根本不需要伪类。
      • 我将其更改为已接受的答案。感谢多年后的跟进。
      【解决方案4】:

      根据您对他人的回答,我相信您的问题实际上是:

      为什么必须在 CSS 中设置伪类的 content 属性,如 与非伪类的内容相反,可以设置在 HTML 还是 CSS?

      原因是:

      • 根据定义,伪类是为页面的 HTML 标记指定的每个元素动态创建的
      • 所有页面元素,包括伪类,都必须具有要显示的内容属性。
      • <p> 这样的HTML 元素也可以,但是您可以使用标记( 与CSS 声明)快速设置它们的内容属性。
      • 但是,与非伪类元素不同,伪类不能在标记本身中被赋予值。
        因此,所有伪类都是不可见的(它们的“内容”属性没有值)除非你告诉它们不可见(通过 CSS 声明赋予它们值)。

      以这个简单的页面为例:

      <body>
      <p> </p>
      </body>
      

      我们知道这个页面不会显示任何内容,因为&lt;p&gt; 元素没有文本。更准确的说法是,&lt;p&gt; 元素的 content 属性没有值

      我们可以很容易地改变这一点,通过在 HTML 标记中设置 h1 元素 的 content 属性

      <body>
      <p>This sentence is the content of the p element.</p>
      </body>
      

      这将在加载时显示,因为 &lt;p&gt; 元素的 content 属性有一个值;该值是一个字符串:

      "This sentence is the content of the p element."
      

      或者,我们可以通过在 CSS 中设置 &lt;p&gt; 元素的 content 属性来显示 &lt;p&gt; 元素:

      p { content: "This sentence is the content of the p element set in the CSS."; }
      

      这两种将字符串注入&lt;p&gt;元素的方式是相同的。

      现在,考虑用伪类做同样的事情:

      HTML:
        <body>
            <p class="text-placeholder">P</p>
        </body>
      
      CSS:
        p:before { content: "BEFORE... " ; }
        p:after { content: " ...and AFTER"; }
      

      结果:

      BEFORE...  P ...and AFTER
      

      最后,想象一下您将如何在不使用 CSS 的情况下完成这个示例。这是不可能的,因为没有办法在 HTML 标记中设置伪类的内容。

      您可能很有创意,并想象这样的事情可能会奏效:

      <p:before>BEFORE... </p>
      <p> P </p>
      <p:after> ...and AFTER</p>
      

      但是,它不是,因为 &lt;p:before&gt;&lt;p:after&gt; 不是 HTML 元素

      总结:

      • 每个标记元素都存在伪类。
      • 默认情况下它们是不可见的,因为它们是在没有内容属性的情况下初始化的。
      • 您不能为带有 HTML 标记的伪类设置 content 属性。
        因此,伪元素的 content 属性必须用 CSS 声明,才能显示出来。

      【讨论】:

      • 这个答案在第一次阅读时非常令人困惑,我不得不第二遍将所有出现的“伪类”替换为“伪元素”(以及所有“伪元素”的现有用途完好无损)。请注意,这两个术语不可互换。
      【解决方案5】:

      在您添加 content: ... 之前,伪元素实际上并不存在

      设置其他样式属性不足以强制浏览器创建元素。

      【讨论】:

      • 但是为什么有这个要求呢? :after 选择器的存在不表明需要一个元素吗?
      • @DaveC content 属性的值是您输入要作为生成元素添加的内容的位置。如果有内容,则无需生成元素。
      • 我强烈反对。使用这种逻辑,添加 content: "" 会导致 :after 标记也消失......事实上,它会显示出来。
      • @DaveC 对不起,我说错了。如果没有 content 属性,则不需要生成伪元素。如您所述,伪元素可以将空字符串作为值。
      • 我完全理解它是以这种方式实现的......也许我应该重新提出我的问题......为什么规范不假设当一个伪元素被定义时,它的内容是:“” ?假设缺少元素有什么好处吗?尤其是当任何没有内容的伪元素都是一种完全死气沉沉的风格时。我想您可以将其隐藏并添加内容: "" 稍后使用不同的样式;然而,这种行为可以通过 display: none 来实现。