CSS 当中的 display

CSS 当中的 display

我们在之前的章节当中介绍了 CSS 当中的 float 属性,今天我们接着上回继续来看 CSS 当中的 display 属性

display 是设计 CSS 版面配置中最重要的属性,每个 HTML 元素都有一个预设的 display 值,不同的元素属性会有不同的预设值,大部分元素的 display 属性,预设值通常是 blockinline 其中一个,若该元素的 display 属性被标示为 block 就被称为『区块元素』,若被标示为 inline 就称为『行内元素』,但是我们在这里并不会介绍它的全部属性,而是只会介绍一些比较常用的,比如 blockinline-blockinlinenone 等,我们就先从 none 开始看起吧

none && visibility

我们都知道,当元素的 display 属性被设置成 none 以后,界面上将不会显示该元素,并且该元素不占布局空间,但是我们仍然可以通过 JavaScript 来操作该元素,至于为什么会这样,其实是涉及到浏览器的渲染原理,如下

浏览器会解析 HTML 标签生成 DOM Tree,解析 CSS 生成 CSSOM,然后将 DOM TreeCSSOM 合成生成 Render Tree,元素在 Render Tree 中对应 0 或多个盒子,然后浏览器以盒子模型的信息布局和渲染界面,更多内容可见 浏览器的渲染机制

而设置为 display: none 的元素则在 Render Tree 中没有生成对应的盒子模型,也就是说节点不会被加入 Render Tree 当中,因此后续的布局、渲染工作自然与它无关,但是 DOM 操作还是可以的,但是这里有个需要注意的地方,那就是我们下面将要介绍的 visibility: hidden 则会被加入 Render Tree 当中,所以如果某个节点最开始是不显示的,设为 display: none 是更优的,我们下面再来看一些与其相关的其他特性

原生默认为 None 的元素

其实浏览器原生元素中有不少自带 display: none 的元素,如 linkscriptstyledialoginput[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 的元素根本不会在界面上渲染,就是连一个像素的都不占,因此自然无法通过鼠标点击命中,而元素也无法获取焦点,那么也不能成为键盘事件的命中目标,当父元素的 displaynone 时,子元素的 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,下面我们再来看看 inlineblockinline-block 这三个比较类似的属性,我们先从一个问题开始看起,那就是行内元素和块级元素有什么异同呢?

可能你觉得这是一个老掉牙的问题,但其实并没有那么简单,对于初学者来说,脑海里的第一印象是 CSSdisplay 属性,值为 inline 时为行内元素,值为 block 时为块级元素,那值为 inline-block 时呢,元素是块级行内元素吗?

其实我们看问题的角度从一开始就错了,错误地站在了 CSS 的角度,而真正正确的是站在 HTML 的角度,因为这里的元素本质上是指的是 HTMLDOM 元素,所以下面我们就来深入的了解一下 inline 元素

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

inline 元素

HTML 中,行内元素是那些仅仅占据定义元素边界的标签空间的元素,而不会去破坏内容流,一个行内元素不会在新的一行开始,而且仅仅占据必要的元素宽度空间,不会多占空间,比如下面这个例子

1
<p>The following span is an <span>inline element</span></p>

我们一眼就可以看出,由于 span 是行内元素,所以上面这段话是会显示在一行当中的,但是如果我们此时将 spandisplay 属性调整成 block 以后,就会发现它将会另起一行来进行展示,那么为什么会这样显示呢?

简单来说就是,因为我们手动的指定了 spandisplay 属性为 block,就相当于告诉浏览器我们需要将一个 inline 元素,渲染成 block box 而不是 inline box,所以这里就涉及到一个新的概念 block-level,下面我们就来看看这个 block-level

block-level

简而言之,在概念上 inlineblock-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>,指明了元素外部的展现类型,这基本是它在流式布局中的作用,例如 inlineblock
    • inline,这个元素生成一个或者多个行内盒子
    • block,这个元素生成一个块元素盒子
  • <display-inside>,指明了元素内部的展现类型,定义了布局 formatting context 的类型(假设它是一个不可替换的元素),例如 tableflexgrid
    • table,元素就像 HTML 当中的 <table> 标签一样表现,它定义了一个 block-level 盒子
    • flex,元素表现形式是块级元素,根据 flexbox model 绘制自己的内容
    • grid,元素表现得像块级元素,根据 grid model 绘制自己的内容
  • <display-legacy>CSS2 使用单关键字的语法为 display 属性设置值,在同一个 layout 模型中,为块级元素和行内元素引用分离的关键词,例如 inline-blockinline-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>,一些布局模型,例如 tableruby,它只在特定的 layout mode 下才会生效,例如 table-row-grouptable-header-group 等等
  • <display-box>,是否展示 box,例如 nonecontents(实验中)

通过上面的列表,我们可以大致发现 CSS 属性 display 是如何影响元素的了,它大多数情况是通过修改 <display-outside><display-inside> 这两个属性值去改变元素外部的流式布局和元素内部的 formatting context,其中 formatting context 会根据 model 类型进行渲染,也因此我们上面的问题也就迎刃而解了,那就是 display 值从 inline 更改为 block 的原理

本质上是改变了 <display-outside>,改变了元素的流式布局,也就是从生成一个 inline 盒子,变为生成一个 block 盒子

特性

最后我们再来看看它们三者之间的特性差异,我们先来看看 block,它比较简单,它可以设置元素为块级元素,可应用盒子模型相关属性,默认 width100%height 自适应,marginpadding 都有效,如果没有占宽或高的子元素存在,则高度为零,这个也是平常很常见的情况

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-spaceingword-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 元素的 widthheight 都是无效的,但是 padding 有效,并且 margin 只有左右有效,上下无效,而且 inline 元素包裹 inline 元素,外层元素的 widthheight 会被内部的撑开,可是使用上面 overWidth 的示例来进行测试

但是如果是被 block/inline-block 元素包裹的 inline 元素,默认 width 超出会自动换行,而 height 则会撑开,如果想要实现强制不换行可以通过 white-space: nowrap 来实现,但是此时超出 width 的部分会有溢出,可以通过 overflow: hiddentext-overflow: ellipsis 配合实现省略显示

# CSS

评论

Your browser is out-of-date!

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

×