冷知识!使用 display: contents 实现幽灵节点?

display: contents 是一个比较陌生的属性,虽然属于 display 这个基本上是最常见的 CSS 属性,但是 contents 这个取值基本不会用到。但是它早在 2016 年就已经得到了 Firefox 的支持。

本文将深入一下这个有意思的属性值。

基本用法

根据 W3C[1] 对 display: contents 的定义。

The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes and text runs as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents (including both its source-document children and its pseudo-elements, such as ::before and ::after pseudo-elements, which are generated before/after the element’s children as normal).

简单翻译一下即是,设置了该属性值的元素本身将不会产生任何盒子,但是它保留其子代元素的正常展示。

看个简单的例子。有如下简单三层结构:


    
        
    

简单的 CSS 如下:

.container {
    width: 200px;
    height: 200px;
    background: #bbb;
}

.wrap {
    border: 2px solid red;
    padding: 20px;
    box-sizing: border-box;
}

.inner {
    border: 2px solid green;
    padding: 20px;
    box-sizing: border-box;
}

表现如下:

冷知识!使用 display: contents 实现幽灵节点?_第1张图片

这个非常好理解,但是如果,我们给中间层的容器添加上 display: contents,再看看效果:


    
        
    
冷知识!使用 display: contents 实现幽灵节点?_第2张图片

可以看到,没有了中间层的 border: 2px solid red 的红色边框,整个 .wrap div 好像不存在一样,但是它的子元素却是正常的渲染了。

重点,设置了display: contents的元素本身不会被渲染,但是其子元素能够正常被渲染

这个属性我一直在思考有什么非常适合的使用点。

总结来说,这个属性适用于那些充当遮罩(wrapper)的元素,这些元素本身没有什么作用,可以被忽略的一些布局场景。也就是幽灵 DOM 节点

充当无语义的包裹框

最近写 React、Vue 的时候,发现这个属性在写 JSX 的时候能有很好的作用,并且也非常符合这个属性本身的定位。

我们在写 DOM 结构时,经常需要输出一段模板,或者需要一些无语义的幽灵节点。

return (
    
        

Title

        
...
    
)

我们只是想输出 .wrap div 内的内容,但是由于框架要求,输出的 JSX 模板必须包含在一个父元素之下,所以不得已,需要添加一个 .wrap 进行包裹,但是这个 .wrap 本身是没有任何样式的。

如果输出的元素是要放在其他 display: flexdisplay: grid 容器之下,加了一层无意义的 .wrap 之后,整个布局又需要重新进行调整,麻烦。

一种方法是使用框架提供的容器 ,它不会向页面插入任何多余节点。

在 Vue 中类似的是