CSS 当中的 display

CSS 当中的 display

我们在之前的章节当中介绍了 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
2
3
4
/* 兼容原生不支持 hidden 属性的浏览器  */
[hidden]{
display: none;
}
1
<span hidden>Hide and Seek: You can't see me!</span>

后代元素不可见

当父元素为设置为 display: none 后,它的后代元素都是不可见的,如下示例

1
2
3
4
5
6
*** START ***
<div style="display:none;">
I'm parent!
<div style="display: block"> I'm son! </div>
</div>
*** END ***

运行后可以发现,页面当中只会显示 *** START *** *** END *** 内容

无法获取焦点

设置为 display: none 后,是无法获取焦点的,比如如下示例,可以使用 TAB 键来进行测试

1
2
<input type="hidden">
<div tabindex="1" style="display:none">hidden</div>

无法响应事件

设置为 display: none 后,是无法响应任何事件的,无论是捕获、命中目标和冒泡阶段均不可以,这是由于 display: none 的元素根本不会在界面上渲染,就是连一个像素的都不占,因此自然无法通过鼠标点击命中,而元素也无法获取焦点,那么也不能成为键盘事件的命中目标,当父元素的 display 为 none 时,子元素的 display 必定为 none,因此元素也没有机会位于事件捕获或冒泡阶段的路径上,因此 display: none 的元素无法响应事件

不影响表单提交

虽然我们无法看到 display: none 的元素,但当表单提交时依然会将隐藏的 input 元素的值提交上去

1
2
3
4
<form>
<input type="hidden" name="id">
<input type="text" name="name" style="display:none">
</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
2
3
4
5
6
<div style="visibility: hidden">
I'm Parent.
<div style="visibility: visible;">
I'm Son.
</div>
</div>

可以发现运行以后,虽然 parent 是使用 visibility: hidden 隐藏了,但是如果子元素设置为 visibility: visible 仍然是可以显示的

可以在冒泡阶段响应事件

由于设置为 visibility: hidden 的元素其子元素可以设置为 visibility: visible,因此隐藏的元素有可能位于事件冒泡的路径上,所以在下面代码中将鼠标移至属性为 .visible 的元素时,.hidden 会的元素响应 hover 事件显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
div{
border: solid 2px blue;
}

.visible{
visibility: visible;
}

.hidden{
visibility: hidden;
}

.hidden:hover{
visibility: visible;
}
1
2
3
4
5
6
<div class="hidden">
I'm Parent.
<div class="visible">
I'm Son.
</div>
</div>

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 元素

这里需要结合我们之前介绍过的 BFC 和 IFC 相关内容来一起进行了解

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
2
3
<a>1</a>
<a>2</a>
<a>3</a>

运行以后可以发现,在页面当中显示的时候,它们三者之前并不是仅仅靠在一起的,而是之间互有距离,那么怎么解决这个问题呢?解决办法有很多种,我们一个一个来看,第一种也是最为简便的方式,就是将它们三者放在一行当中,如下

1
<a>1</a><a>2</a><a>3</a>

第二种方式就是利用 margin-left: -8px,也就是 marginq 负值实现,也可以在外层使用 letter-spaceing 和 word-spaceing 为负值的方式实现(此种试子元素需要重置被设置属性)

1
2
3
4
<div class="inline">inline01</div>
<div class="inline">inline02</div>
<div class="inline">inline03</div>
<div class="inline">inline04</div>
1
2
3
4
5
6
7
.inline{
display: inline;
background: red;
margin: 0px;
padding: 10px;
margin-left: -8px;
}

第三种方式就是在包裹 inline 元素的外层元素加上 font-size: 0px 和 -webkit-text-size-adjust: none 来进行实现

1
2
3
4
<div class="overWidth">
<a>1</a>
<a>123456</a>
</div>
1
2
3
4
5
6
7
8
a{
background: red;
font-size: 14px;
}
.overWidth{
font-size: 0px;
-webkit-text-size-adjust: none;
}

此外,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 配合实现省略显示

# CSS

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×