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

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

基本用法

根据 W3C 对 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).

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

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

<div class="container">
<div class="wrap">
<div class="inner"></div>
</div>
</div>

简单的 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,再看看效果:

<div class="container">
<div class="wrap" style="display: contents">
<div class="inner"></div>
</div>
</div> 

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

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

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

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

充当无语义的包裹框

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

我们在写 React、RN 时,经常需要输出一段模板。

return (
<div class="wrap">
<h2>Title</h2>
<div>...</div>
</div>
)

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

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

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

在 Vue 中类似的是 <template> 元素, <template> 也是不会被渲染在 DOM 树中,查看页面结构也无法看到,但是 display: contents 是存在于页面结构中的,只是没有生成任何盒子。

这个多出来的父元素其实是没必要的。这个时候,我们也可以添加上 display: contents,像是这样:

return (
<div class="wrap" style="display: contents">
<h2>Title</h2>
<div>...</div>
</div>
)

这样,它既起到了包裹的作用,但是在实际渲染中,这个 div 其实没有生成任何盒子,一举两得。并且像一些 flex 布局、grid 布局,也不会受到影响。

Codepen Demo -- display: contents | display: flex 的穿透影响

让代码更加符合语义化

考虑这个非常实际的场景,现在我们的页面上充斥了大量的可点击按钮,或者点击触发相应功能的文字等元素。但是,从语义上而言,它们应该是一个一个的 <button>,但是实际上,更多时候我们都是使用了 <p>、<div>、<a> 等标签进行了模拟,给他们加上了相应的点击事情而已。

像是下面这样,虽然没什么问题,但是相对而言不那么符合语义化:

<p class="button">
Button
</p>
<p class="button">
Click Me
</p>
.button {
width: 120px;
line-height: 64px;
text-align: center;
background-color: #ddd;
border: 2px solid #666;
}

我们不使用 <button> 的原因有很多,<button> 相对 div 而言没那么好控制,且会引入很多默认样式。但是,有了 display: contents,我们可以让我们的代码既符合语义化,同时不需要去解决 <button> 带来的一些样式问题:

<p class="button">
<button style="display: contents">
Button
</button>
</p>
<p class="button">
<button style="display: contents">
Click Me
</button>
</p>

添加了 <button style="display: contents">Click Me</button> 的包裹,不会对样式带来什么影响,button 也不会实际渲染在页面结构中,但是页面的结构语义上好了不少。

CodePen Demo -- Button with display: contents

对于对页面结构、语义化有强迫症的一些同学而言,灵活运用这个属性可以解决很多问题。

在替换元素及表单元素中一些有意思的现象

display: contents 并非在所有元素下的表现都一致。

对于可替换元素及大部分表单元素,使用 display: contents 的作用类似于 display: none

也就是说对于一些常见的可替换元素、表单元素:

  • <br>
  • <canvas>
  • <object>
  • <audio>
  • <iframe>
  • <img>
  • <video>
  • <frame>
  • <input>
  • <textarea>
  • <select>

作用了 display: contents 相当于使用了 display: none ,元素的整个框和内容都没有绘制在页面上。

<button> 的一些异同

与其他表单元素不一样,正常而言,添加了 display: contents 相当于被隐藏,不会被渲染。但是实际运用过程中发现,<button></button> 如果包裹了内容,其一些可继承样式还是会被子内容继承。这个实际使用的过程中需要注意一下。

对 A11Y 的影响

在一些外文文档中有一些讨论是关于 display: contents 的使用会影响到页面的可访问性。例如作用了 display: contents 的容器及列表,会对页面的可访问性带来一些意外结果。

这个我看暂时没有明确的结论,如果你的页面对可访问性的要求很高,具体使用的此属性的话也是需要注意一下这一点。

CSS 中类似的一些影响布局的属性

CSS 本身其实也在一直在努力,增加了各种属性去让我们在布局上有更多的空间与控制权。总而言之给我的感受是让 CSS 更加的像是一个完整的工程而不仅仅只是展现样式。

类似的一些有意思的属性:

CAN I USE

看看兼容性

不算太惨淡,但也不算全面普及。考虑用在一些渐进增强的场景当中。

参考

最后

好了,本文到此结束,希望对你有帮助 :)

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

更多精彩有趣的 CSS 效果,欢迎来这里看看 CSS 灵感 -- 在这里找到写 CSS 的灵感

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

巧用 display: contents 增强页面语义的更多相关文章

  1. 巧用location.hash保存页面状态

    在我们的项目中,有大量ajax查询表单+结果列表的页面,由于查询结果是ajax返回的,当用户点击列表的某一项进入详情页之后,再点击浏览器回退按钮返回ajax查询页面,这时大家都知道查询页面的表单和结果 ...

  2. vue-lazy-render: 延迟渲染大组件,增强页面切换流畅度

    最近用element来做项目,在开发的过程中,突然发现页面的操作和切换在数据量大的时候相当卡,后来提了个issue,在furybean解答后才知道,我每个单元格都加了tooltip,会生成大量的节点, ...

  3. 巧用同步请求处理react页面刷新问题

    很多时候,我们会遇到这种情况,组件加载需要请求后台数据,然后填充组件.那么我们一般会这样处理:如[使用异步请求的方式]代码: 加载组价的时候,未获得数据,render一个空的div: 然后异步请求数据 ...

  4. 巧用 Img / JavaScript 采集页面数据

    摘要: 当我们有一个新内容时(例如新功能.新活动.新游戏.新文章),作为运营人员总是迫不及待地希望能尽快传达到用户,因为这是获取用户的第一步.也是最重要的一步. 点此查看原文:http://click ...

  5. css架构技巧

    1. 写一个reset.css 用于清除浏览器标签默认样式并定义全局样式,这样就不会因为浏览器默认样式出现问题,因为不同浏览器的默认样式还是不一样的

  6. ie、firefox、chrome中关于style="display:block" 引发的页面布局错乱的解决办法

    ie.firefox.chrome中关于style="display:block" 引发的页面布局错乱的解决办法: table中tr 添加style="display:b ...

  7. display值的分类

    整体来讲,display的值可以分为6个大类,1个全局类,一共是7大类: 外部值 内部值 列表值 属性值 显示值 混合值 全局值 外部值,指的是这些值只会直接影响一个元素的外部表现,而不影响元素里面的 ...

  8. 语义化HTML

    一.怎样语义化html结构 语义化是指用合理HTML标签以及其特有的属性去格式化文档内容. 正确语义化----结构(html)才是重点,样式(css)是用来修饰结构的.所以要先确定html,确定标签, ...

  9. [转] 使用CSS3 will-change提高页面滚动、动画等渲染性能 ---张鑫旭

    一.先来看一个例子 下面这个例子来自某外文,我这里简单转述下. 视差滚动现在不是挺流行的嘛,然后Chris Ruppel当其使用background-attachment: fixed实现背景图片不随 ...

随机推荐

  1. 手写实现java栈结构,并实现简易的计算器(基于后缀算法)

    一.定义 栈是一种线性表结构,栈结构中有两端,对栈的操作都是对栈的一端进行操作的,那么被操作的一端称为栈顶,另一端则为栈底.对栈的操作其实就是只有两种,分别是入栈(也称为压栈)和出栈(也称为弹栈).入 ...

  2. C#并发编程之初识并行编程

    写在前面 之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容,因为手中商城的重构工作量较大,一时之间无法抽出时间.近日,这套系统已有阶段性成果,所以准备写一下Parallel ...

  3. ssm整合后打印日志查看执行sql语句

    mybatis.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configura ...

  4. ssh框架整合时的延迟加载问题(no session问题)的分析以及解决方案

    当我们整合完三大框架,并采用hibernate的延迟加载方案时,会出现如下的异常: 现在对这个异常进行分析,如下图所示(模拟通过id查询用户信息的过程): 上图分析了为什么会出现no session的 ...

  5. 记录关于Android多线程的一个坑

    最近在写项目的时候由于联网用得比较频繁,就简单地封装了一个工具类,省得每次联网得时候都要把联网配置写一遍,代码如下: public class okhttp_plus { public static ...

  6. FPGA六位共阳极数码管动态显示

    `timescale 1ns/1ps module adc_dis( clk , rst_n , sm_seg , sm_bit ); input clk;//50HZ input rst_n; :] ...

  7. [poj1741 Tree]树上点分治

    题意:给一个N个节点的带权树,求长度小于等于K的路径条数 思路:选取一个点作为根root,假设f(root)是当前树的答案,那么答案来源于两部分: (1)路径不经过root,那么就是完全在子树内,这部 ...

  8. SpringBoot基础实战系列(二)springboot解析json与HttpMessageConverter

    SpringBoot解析Json格式数据 @ResponseBody 注:该注解表示前端请求后端controller,后端响应请求返回 json 格式数据前端,实质就是将java对象序列化 1.创建C ...

  9. 「雕爷学编程」Arduino动手做(17)---人体感应模块

    37款传感器和模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器与模块,依照实践出真知(动手试试)的理念,以学习和交流为目的,这里准备 ...

  10. 「雕爷学编程」Arduino动手做(13)——触摸开关模块

    37款传感器和模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器与模块,依照实践出真知(动手试试)的理念,以学习和交流为目的,这里准备 ...