我们在之前的章节当中介绍了 CSS
当中的 float 属性,今天我们接着上回继续来看 CSS
当中的 display
属性
display
是设计 CSS
版面配置中最重要的属性,每个 HTML
元素都有一个预设的 display
值,不同的元素属性会有不同的预设值,大部分元素的 display
属性,预设值通常是 block
或 inline
其中一个,若该元素的 display
属性被标示为 block
就被称为『区块元素』,若被标示为 inline
就称为『行内元素』,但是我们在这里并不会介绍它的全部属性,而是只会介绍一些比较常用的,比如 block
、inline-block
、inline
和 none
等,我们就先从 none
开始看起吧
none && visibility
我们都知道,当元素的 display
属性被设置成 none
以后,界面上将不会显示该元素,并且该元素不占布局空间,但是我们仍然可以通过 JavaScript
来操作该元素,至于为什么会这样,其实是涉及到浏览器的渲染原理,如下
浏览器会解析
HTML
标签生成DOM Tree
,解析CSS
生成CSSOM
,然后将DOM Tree
和CSSOM
合成生成Render Tree
,元素在Render Tree
中对应0
或多个盒子,然后浏览器以盒子模型的信息布局和渲染界面,更多内容可见 浏览器的渲染机制
而设置为 display: none
的元素则在 Render Tree
中没有生成对应的盒子模型,也就是说节点不会被加入 Render Tree
当中,因此后续的布局、渲染工作自然与它无关,但是 DOM
操作还是可以的,但是这里有个需要注意的地方,那就是我们下面将要介绍的 visibility: hidden
则会被加入 Render Tree
当中,所以如果某个节点最开始是不显示的,设为 display: none
是更优的,我们下面再来看一些与其相关的其他特性
原生默认为 None 的元素
其实浏览器原生元素中有不少自带 display: none
的元素,如 link
,script
,style
,dialog
,input[type=hidden]
等
hidden 属性
在 HTML5
中新增了 hidden
布尔属性,它可以让开发者自定义元素的隐藏,如下
1 | /* 兼容原生不支持 hidden 属性的浏览器 */ |
1 | <span hidden>Hide and Seek: You can't see me!</span> |
后代元素不可见
当父元素为设置为 display: none
后,它的后代元素都是不可见的,如下示例
1 | *** START *** |
运行后可以发现,页面当中只会显示 *** START *** *** END ***
内容
无法获取焦点
设置为 display: none
后,是无法获取焦点的,比如如下示例,可以使用 TAB
键来进行测试
1 | <input type="hidden"> |
无法响应事件
设置为 display: none
后,是无法响应任何事件的,无论是捕获、命中目标和冒泡阶段均不可以,这是由于 display: none
的元素根本不会在界面上渲染,就是连一个像素的都不占,因此自然无法通过鼠标点击命中,而元素也无法获取焦点,那么也不能成为键盘事件的命中目标,当父元素的 display
为 none
时,子元素的 display
必定为 none
,因此元素也没有机会位于事件捕获或冒泡阶段的路径上,因此 display: none
的元素无法响应事件
不影响表单提交
虽然我们无法看到 display: none
的元素,但当表单提交时依然会将隐藏的 input
元素的值提交上去
1 | <form> |
display 变化时将触发 reflow
如果撇开 display: none
的话,我们都知道 block
表示元素位于 BFC
中,而 inline
则表示元素位于 IFC
中,也就是说 display
就是用于设置元素所属的布局上下文,若修改 display
值则表示元素采用的布局方式已发生变化,因此 display
变化时将触发 reflow
下面我们再来看一个经常会与 display: none
放在一起说的 visibility: hidden
,虽然他们都可以隐藏元素,但是它与 display: none
最为主要的区别就是 visibility: hidden
可以隐藏某个元素,但隐藏的元素仍需占用与未隐藏之前一样的空间,也就是说,该元素虽然被隐藏了但仍然会影响布局,它与 display: none
表现一致的行为有以下这些
- 和
display: none
一样无法获得焦点 - 和
display: none
一样不妨碍form
表单的提交
下面我们主要来看看两者之间的一些差异点
后代元素可以选择可见
与 display: none
不同的是,如果父元素为 visibility: hidden
,而子元素设置为 visibility: visible
的话,子元素是会显示的,如下
1 | <div style="visibility: hidden"> |
可以发现运行以后,虽然 parent
是使用 visibility: hidden
隐藏了,但是如果子元素设置为 visibility: visible
仍然是可以显示的
可以在冒泡阶段响应事件
由于设置为 visibility: hidden
的元素其子元素可以设置为 visibility: visible
,因此隐藏的元素有可能位于事件冒泡的路径上,所以在下面代码中将鼠标移至属性为 .visible
的元素时,.hidden
会的元素响应 hover
事件显示
1 | div{ |
1 | <div class="hidden"> |
visibility 变化不会触发 reflow
这个我们在上面也提到过,因为 visibility: hidden
会被加入 Render Tree
,而又当 visible
设置为 hidden
时,不会改变元素布局相关的属性,因此不会触发 reflow
,只是静静地和其他渲染变化一起等待浏览器定时重绘界面
inline && block && inline-block
看完了 none
,下面我们再来看看 inline
,block
,inline-block
这三个比较类似的属性,我们先从一个问题开始看起,那就是行内元素和块级元素有什么异同呢?
可能你觉得这是一个老掉牙的问题,但其实并没有那么简单,对于初学者来说,脑海里的第一印象是 CSS
的 display
属性,值为 inline
时为行内元素,值为 block
时为块级元素,那值为 inline-block
时呢,元素是块级行内元素吗?
其实我们看问题的角度从一开始就错了,错误地站在了 CSS
的角度,而真正正确的是站在 HTML
的角度,因为这里的元素本质上是指的是 HTML
的 DOM
元素,所以下面我们就来深入的了解一下 inline
元素
inline 元素
在 HTML
中,行内元素是那些仅仅占据定义元素边界的标签空间的元素,而不会去破坏内容流,一个行内元素不会在新的一行开始,而且仅仅占据必要的元素宽度空间,不会多占空间,比如下面这个例子
1 | <p>The following span is an <span>inline element</span></p> |
我们一眼就可以看出,由于 span
是行内元素,所以上面这段话是会显示在一行当中的,但是如果我们此时将 span
的 display
属性调整成 block
以后,就会发现它将会另起一行来进行展示,那么为什么会这样显示呢?
简单来说就是,因为我们手动的指定了 span
的 display
属性为 block
,就相当于告诉浏览器我们需要将一个 inline
元素,渲染成 block box
而不是 inline box
,所以这里就涉及到一个新的概念 block-level
,下面我们就来看看这个 block-level
block-level
简而言之,在概念上 inline
和 block-level
元素有以下区别
Content Model
,行内元素仅仅包含数据和其他的行内元素,比如不能在inline elements
的内部去添加block elements
Formatting
,默认情况下行内元素不会在文档流的开始位置强制生成一个新行,而如果是block
元素的话,正好与之相反,会很显著地另起一行来进行展示(但是可以使通过CSS
来进行改变)
HTML
元素通常要么是 block-level
元素,要么是 inline elements
,一个 block-level
元素,会占据它的父元素,也就是容器的全部空间,从而新建了一个 block
,通常从一个新的行开始,占据可用空间的全部宽度(从左到右尽可能的拉伸),但是至于为什么会这样渲染,我们就需要更为深入的来了解一下 Level 3
规范中定义的 display
属性
display 属性
display
属性指明了元素渲染盒子的类型,在 HTML
中,默认 display
属性值取自 HTML
规范或浏览器/用户默认样式表中描述的行为,关于 display
值有以下几种分类,这里我们主要看前面三点
<display-outside>
,指明了元素外部的展现类型,这基本是它在流式布局中的作用,例如inline
,block
inline
,这个元素生成一个或者多个行内盒子block
,这个元素生成一个块元素盒子
<display-inside>
,指明了元素内部的展现类型,定义了布局formatting context
的类型(假设它是一个不可替换的元素),例如table
,flex
,grid
table
,元素就像HTML
当中的<table>
标签一样表现,它定义了一个block-level
盒子flex
,元素表现形式是块级元素,根据flexbox model
绘制自己的内容grid
,元素表现得像块级元素,根据grid model
绘制自己的内容
<display-legacy>
,CSS2
使用单关键字的语法为display
属性设置值,在同一个layout
模型中,为块级元素和行内元素引用分离的关键词,例如inline-block
,inline-table
等等inline-block
,元素生成一个块元素盒子,它会被周围的内容包裹,就仿佛它是一个inline box
一样,等价于inline flow-root
inline-table
,它表现地就像HTML
标签一样,但是是一个inline box
,而不是block-level
盒子,在table
盒子内部是块级内容,等价于inline table
inline-flex
,行内元素表现,布局内容时根据flexbox
模型布局,等价于inline flex
inline-grid
,行内元素展现,布局内容时根据grid
模型
<display-listitem>
,元素生成了用来存放内容的块盒子以及一个分离的list-item
行内盒子,例如list-item
<display-internal>
,一些布局模型,例如table
和ruby
,它只在特定的layout mode
下才会生效,例如table-row-group
,table-header-group
等等<display-box>
,是否展示box
,例如none
,contents
(实验中)
通过上面的列表,我们可以大致发现 CSS
属性 display
是如何影响元素的了,它大多数情况是通过修改 <display-outside>
,<display-inside>
这两个属性值去改变元素外部的流式布局和元素内部的 formatting context
,其中 formatting context
会根据 model
类型进行渲染,也因此我们上面的问题也就迎刃而解了,那就是 display
值从 inline
更改为 block
的原理
本质上是改变了 <display-outside>
,改变了元素的流式布局,也就是从生成一个 inline
盒子,变为生成一个 block
盒子
特性
最后我们再来看看它们三者之间的特性差异,我们先来看看 block
,它比较简单,它可以设置元素为块级元素,可应用盒子模型相关属性,默认 width
为 100%
,height
自适应,margin
、padding
都有效,如果没有占宽或高的子元素存在,则高度为零,这个也是平常很常见的情况
inline-block
元素是 inline + block
的合体,它的 margin、padding、width 和 height 都有效,但是 block/inline-block
元素包裹的 inline-block
元素,默认超过 width
会换行,而 hieght
则会撑开,但是可以通过 white-space: nowrap
强制不换行,但是就不能实现文本省略显示了,另外它也存在着和我们下面将要介绍的 inline
一样的问题,那就是存在 8
像素的间隔问题
关于 inline,行内元素或者通过 display: inline
修饰为行内元素的都具有行内元素的行为,多个 inline
元素会排成一行,但是它最为出名的问题那就是并列的多个 inline
元素之间会存在 8
个像素左右的间隔,比如下面这个例子
1 | <a>1</a> |
运行以后可以发现,在页面当中显示的时候,它们三者之前并不是仅仅靠在一起的,而是之间互有距离,那么怎么解决这个问题呢?解决办法有很多种,我们一个一个来看,第一种也是最为简便的方式,就是将它们三者放在一行当中,如下
1 | <a>1</a><a>2</a><a>3</a> |
第二种方式就是利用 margin-left: -8px
,也就是 marginq
负值实现,也可以在外层使用 letter-spaceing
和 word-spaceing
为负值的方式实现(此种试子元素需要重置被设置属性)
1 | <div class="inline">inline01</div> |
1 | .inline{ |
第三种方式就是在包裹 inline
元素的外层元素加上 font-size: 0px
和 -webkit-text-size-adjust: none
来进行实现
1 | <div class="overWidth"> |
1 | a{ |
此外,inline
元素的 width
和 height
都是无效的,但是 padding
有效,并且 margin
只有左右有效,上下无效,而且 inline
元素包裹 inline
元素,外层元素的 width
和 height
会被内部的撑开,可是使用上面 overWidth 的示例来进行测试
但是如果是被 block/inline-block
元素包裹的 inline
元素,默认 width
超出会自动换行,而 height
则会撑开,如果想要实现强制不换行可以通过 white-space: nowrap
来实现,但是此时超出 width
的部分会有溢出,可以通过 overflow: hidden
和 text-overflow: ellipsis
配合实现省略显示