为什么 :before 和 :after 伪元素需要 'content' 属性?

2022-01-30 00:00:00 pseudo-element css css-content

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

Given the following scenario, why does the :after selector require a content property to function?

.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 属性之前,您是看不到伪元素的:

Notice how you do not see the pseudo element until you specify the content property:

.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 调试器中看到样式;但是,它们不会显示在页面上.

Why is this the intended functionality? You would think that the display block would force the element to show up. Oddly enough, you can actually see the styles inside web debuggers; however, they do not display on the page.

推荐答案

你需要为每个 ::before 和/或 content: '' 声明的原因code>::after 伪元素是因为 content 的初始值是 normal,计算为 none::before::after 伪元素.请参阅 规范.

The reason you need a content: '' declaration for each ::before and/or ::after pseudo-element is because the initial value of content is normal, which computes to none on the ::before and ::after pseudo-elements. See the spec.

content 的初始值不是空字符串,而是 ::before::after 伪元素,是双重的:

The reason the initial value of content isn't an empty string but a value that computes to none for the ::before and ::after pseudo-elements, is twofold:

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

  1. Having empty inline content at the start and end of every element is rather silly. Remember that the original purpose of the ::before and ::after pseudo-elements is to insert generated content before and after the main content of an originating element. When there's no content to insert, creating an additional box just to insert nothing is pointless. So the none value is there to tell the browser not to bother with creating an additional box.

使用空的 ::before::after 伪元素来创建额外的盒子的做法是相对较新的,并且一些纯粹主义者出于这个原因,甚至可能将其称为 hack.

The practice of using empty ::before and ::after pseudo-elements to create additional boxes for the sole purpose of layout aesthetics is relatively new, and some purists might even go so far as to call it a hack for this reason.

在每个元素的开头和结尾都有空的内联内容意味着每个(非替换的)元素——包括 htmlbody——默认情况下都会生成的不是一个框,而是最多 三个 个框(对于已经生成的元素不仅仅是主框,例如具有列表样式的元素,还有 更多 个框).您将实际使用每个元素的两个额外框中的多少?这可能会使布局成本三倍而收益微乎其微.

Having empty inline content at the start and end of every element means that every (non-replaced) element — including html and body — would by default generate not one box, but up to three boxes (and more in the case of elements that already generate more than just the principal box, like elements with list styles). How many of the two extra boxes per element will you actually use? That's potentially tripling the cost of layout for very little gain.

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

Realistically, even in this decade, less than 10% of the elements on a page will ever need ::before and ::after pseudo-elements for layout.

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

And so these pseudo-elements are made opt-in — because making them opt-out is not only a waste of system resources, but just plain illogical given their original purpose. The performance reason is also why I do not recommend generating pseudo-elements for every element using ::before, ::after.

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

But then you might ask: why not have the display property default to none on ::before, ::after? Simple: because the initial value of display is not none; it is inline. Having inline compute to none on ::before, ::after is not an option because then you could never display them inline. Having the initial value of display be none on ::before, ::after is not an option because a property can only have one initial value. (This is why the initial value of content is always normal and it is simply defined to compute to none on ::before, ::after.)

相关文章