HTML中元素分类以及margin: 0 auto的初识

107 阅读20分钟

HTML4中元素的分类以及嵌套关系

HTML4中元素的分类

在早期(HTML5之前)的HTML规范中, 将元素分为了2类

Block-level element: 块级元素

inline elements: 内联元素

也就是说在HTML早期的规范中, 我们并没有inline-level这个名词, 使用的是inline element.

需要注意, 这个分类是由HTML定义的, 只不过在HTML规范中允许css指定元素是以block-level 还是 inline 方式进行渲染. CSS通过 VFM(视觉格式模型) 渲染我们的具体元素.

CSS控制着渲染的具体行为, HTML我个人觉得只是概述性的表述了我们元素的分类, 而在这种分类下, 我们的CSS行为具体如何处理, 需要翻阅我们CSS的相关规范

CSS通过使用了display来设置元素的渲染方式, 但是它所设置的 那些值 都会归纳在HTML中的这2类中. CSS为了实现更细粒度的管理, 以及兼容性, 他提供了很多的属性值. 例如block, inline-block, inline, table等. 而CSS对HTML的Block-level / inline 有着属于自己的诠释

具体可参考: www.w3.org/TR/CSS22/vi…

css对于元素的分类限制了我们css中的可以使用的美化属性的功能, 例如width, height, text-align会否有效

7.5.3 Block-level and inline elements(块级和内联元素)

Certain HTML elements that may appear in BODY are said to be "block-level" while others are "inline" (also known as "text level"). The distinction is founded on several notions:

在body中可能会出现某些HTML元素被称为 "block-level" 有一些称之为"inline" (或者称为 "text-level"). 这种区分来源于下面的这些概念

Content model (内容模型)

Generally, block-level elements may contain inline elements and other block-level elements. Generally, inline elements may contain only data and other inline elements. Inherent in this structural distinction is the idea that block elements create "larger" structures than inline elements.

通常, 块级元素可能包含内联元素或者其他的块级元素. 通常而言, 内联元素可能只包含一个数据, 或者其他的内联元素. 这种结构上的区别可以当成块元素可以比内联元素创建更大的结构(英语不好, 不太清楚如何翻译)

Formatting (格式)

By default, block-level elements are formatted differently than inline elements. Generally, block-level elements begin on new lines, inline elements do not. For information about white space, line breaks, and block formatting, please consult the section on text.

在默认情况下, 块级元素在格式上和内联元素时不同的. 通常而言, 块级元素从新的一行开始, 而内联元素不是这样. 有关这种空格, 换行, 或者块格式的信息可以参考这个text内容

Directionality(方向性)

For technical reasons involving the [UNICODE] bidirectional text algorithm, block-level and inline elements differ in how they inherit directionality information. For details, see the section on inheritance of text direction.

Style sheets provide the means to specify the rendering of arbitrary elements, including whether an element is rendered as block or inline. In some cases, such as an inline style for list elements, this may be appropriate, but generally speaking, authors are discouraged from overriding the conventional interpretation of HTML elements in this way.

由于涉及到unicode的双向文本算法的原因, 块级元素和内联元素在如何继承他们的方向信息是不同的. 更多细节, 可以参考 inheritance of text direction.

样式表提供了声明一个元素如何去渲染, 这种渲染包括了一个元素是渲染成block 或者是 inline.在一些例子中, 例如列表元素设置为内联样式, 这或许是合适的. 但是通常而言, 开发者不推荐通过这种覆盖的方式来转换HTML元素的表示.

来源于:www.w3.org/TR/html401/…

CSS中对于盒子的行为定义

在学习这里的部分时, 即使我看了好几遍的文档关于VFM中block相关的定义, 依旧对其存在不理解, 偶然翻阅张鑫旭前辈的《CSS世界》中如下这段话, 突然对这里的概念有着更加深刻的理解.

于是,按照display的属性值不同,值为block的元素的盒子实际由外在的“块级盒子”和内在的“块级容器盒子”组成,值为inline-block的元素则由外在的“内联盒子”和内在的“块级容器盒子”组成,值为inline的元素则内外均是“内联盒子”。

...

如果遵循这种理解,display:block应该脑补成display:block-block,display:table应该脑补成display:block-table,我们平时的写法实际上是一种简写 --《CSS世界》

根据张鑫旭前辈这里的解释, 可以很好的诠释我们上面你的内容. HTML4中元素只有block-level 以及 inline. 而CSS对每个元素的外部盒子定义这种行为, 也就是是否需要独占一行, 或者行内并排.

而对盒子的内容, 通过一个新的盒子定义, 也就是我们上面的容器盒子, CSS对容器盒子进行新的定义, 来确定是否容器是允许设置width, height, padding, margin的数据.

block element

接下来我们尝试阅读一下CSS2.2中关于 VFM 的内容.

Containing blocks

In CSS 2.2, many box positions and sizes are calculated with respect to the edges of a rectangular box called a containing block. In general, generated boxes act as containing blocks for descendant boxes; we say that a box "establishes" the containing block for its descendants. The phrase "a box's containing block" means "the containing block in which the box lives," not the one it generates.

Each box is given a position with respect to its containing block, but it is not confined by this containing block; it may overflow.

容器块(在张鑫旭前辈书中以 容器块进行翻译, 我们这里也翻译成容器块)

在CSS2.2中, 一些盒子的位置以及大小的计算是基于一个被称之为容器块(containing block) 的矩形盒子的边缘进行的. 通常, 生成的盒子充当子元素盒子的容器块. 我们称这个盒子建立了它子元素的容器块.

常说 "一个盒子的容器块" 其实就是说"这个盒子所在的这个容器块". 而不是它生成的那个.

每个盒子的位置是相对于它的容器块的获取的. 但是他不受这个容器块的影响, 他可能 溢出

从这里我们可以知道盒子的位置以及大小是基于容器块计算的, 所以它就是我们设置width, height的相关盒子.

Controlling box generation

The following sections describe the types of boxes that may be generated in CSS 2.2. A box's type affects, in part, its behavior in the visual formatting model. The 'display' property, described below, specifies a box's type.

Certain values of the 'display' property cause an element of the source document to generate a principal box that contains descendant boxes and generated content and is also the box involved in any positioning scheme. Some elements may generate additional boxes in addition to the principal box: 'list-item' elements. These additional boxes are placed with respect to the principal box.

控制盒子(box)的生成

这下面的这部分内容描述了在CSS2.2中生成一个盒子的种类. 盒子的类型会在CSS的视觉格式模型中部分影响它的行为, 通过display属性来描述一个指定的盒子类型

display属性的值会使得源文档中的一个元素生成一个主要的盒子(principal box). 这个盒子会包含子元素的盒子以及生成内容. 这个盒子我们称之为主盒子(principal box). 同时这个盒子也会参与定位的计算. 一些元素除了主盒子之外, 还会生成其他的盒子, 例如list-item元素(会有一个列表符号的附加盒子). 这些附加的盒子是相对于主盒子进行放置的

Block-level elements – those elements of the source document that are formatted visually as blocks (e.g., paragraphs) – are elements which generate a block-level principal box. Values of the 'display' property that make an element block-level include: 'block', 'list-item', and 'table'. Block-level boxes are boxes that participate in a block formatting context.

块级元素: 源文档中的这些元素(例如: p元素)会在视觉上以一个块的格式进行显示. 这些元素将会生成一个块级的主盒子. 我们可以通过设置display属性的值: block, table,list-item 来确保元素为块级. 所谓的块级盒子就是这个盒子在BFC(块级上下文)中进行计算.

In CSS 2.2, a block-level box is also a block container box unless it is a table box or the principal box of a replaced element. A block container box either contains only block-level boxes or establishes an inline formatting context and thus contains only inline-level boxes. An element whose principal box is a block container box is a block container element. Values of the 'display' property which make a non-replaced element generate a block container include 'block', 'list-item' and 'inline-block'. Not all block container boxes are block-level boxes: non-replaced inline blocks and non-replaced table cells are block containers but are not block-level. Block-level boxes that are also block containers are called block boxes.

在CSS2.2中, 一个块级盒子总是一个块级容器的盒子. 除非他是一个table的盒子或者这个主盒子是一个可替换元素. 一个块级容器盒子只会是包含了一个块级盒子(block-level) 或者是 建立了一个内联格式上下文也就是只包含内联级盒子(inline-level box). 一个元素如果主盒子是块级容器盒子, 那么它就是一个块级容器元素. 我们可以通过display属性的值来确保一个非替换元素生成一个块级容器. 这些值有: block, list-item, inline-block. 不是所有的块级容器盒子都是一个块级盒子: 例如不可替换的inline block以及不可替换的table cell 他们都是一个块级容器, 但是不是块级. 具有块级容器的块级盒子被称为块盒子.

所以这里也就说明了我们的一个block元素, 其实在css中是通过2个盒子进行表示的. 一个盒子表示了这个元素是属于block-level, 还是inline, 而内部具有一个专门计算的盒子, 我们将其称之为 container.

所以对于那些display: inline-block 而言, 他们的外部表现为inline, 而内部的元素被container包裹, 使得width, height有效, 参与BFC中的计算.

inline element

说了block-level, 再看看css2.2中对于inline-level的定义吧

Inline-level elements are those elements of the source document that do not form new blocks of content; the content is distributed in lines (e.g., emphasized pieces of text within a paragraph, inline images, etc.). The following values of the 'display' property make an element inline-level: 'inline', 'inline-table', and 'inline-block'. Inline-level elements generate inline-level boxes, which are boxes that participate in an inline formatting context.

(内联级元素是源文档中不会形成新的内容块的那些元素; 内容分布在行中(例如段落中的强调文本, 内联图像等). 可以通过设置 display 属性值使元素成为内联级: 例如( inline, inline-tableinline-block) 内联级元素生成内联级盒子, 这些框是参与内联格式化上下文的框)

An inline box is one that is both inline-level and whose contents participate in its containing inline formatting context. A non-replaced element with a 'display' value of 'inline' generates an inline box. Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements) are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.

一个内联盒子是既有内联级 也有包含了内联上下文的盒子. 具有display为inline的一个不可替换元素是一个内联盒子. 不是内联盒子的内联级盒子(例如可替换的内联级元素, 或者inline-block元素, 或者inline-table元素) 被称之为原子内联级盒子. 因为他们在内联上下文中计算是作为一个单一不透明的盒子参与

这里可以看见我们的inline元素也存在2种类型的框.

外部都是表现为 inline-level 的特性, 也就是可以在一行中进行并排

但是内部可以 内联上下文 方式的计算, 亦或者是block中提到的container, 这个container具有width, height的效果.

而后者的这个规范也给了一个专门的名称, 那就是 原子内联级别盒子(atomic inline-level box), 所谓的原子, 就是一个最小单位, 是一个整体. 这会导致他们在具体排布的时候, 不会出现内容换行.

所以我个人觉得inline-block属于inline-level (^_^) . 这也就意味着对于那些w3c中对于inline-level的设置, 是可以用于display: inline-block 的元素上的, 后面我们可以看下text-align 的说明

例如:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    span{
      border: 2px solid green;
    }
    em {
      border: 2px solid blue;
      display: inline-block
    }
  </style>
</head>
<body>
<P>
  这里会生成一个匿名的行级框.
  <span>这是一个行级的span框, 加长一些内容....</span>
  这里是在span之后生成的匿名行级框
  <em>这是一个原子化的行级框, 它内部具有行级特性, 但是无法折行</em>
</P>
</body>
</html>

元素的嵌套

明确了block和inline的概念之后, 我们可以了解一下早期HTML如何规范每个组件的嵌套关系了

在上面的HTML4对于元素的分类中, 我们知道元素之间的嵌套关系.

Generally, block-level elements may contain inline elements and other block-level elements. Generally, inline elements may contain only data and other inline elements..

通常, 块级元素可能包含内联元素或者其他的块级元素. 通常而言, 内联元素可能只包含一个数据, 或者其他的内联元素

检测html嵌套是否符合标准: validator.w3.org/nu/#textare…

但在学习这部分内容之前需要注意: 浏览器对非标准行为有非常非常非常非常多的兼容,这个不能说明写出来的结构是合法的

早期的HTML是通过DTD 或者说 SGML的方式来约束我们的标签结构

HTML早期的dtd文档: www.w3.org/TR/html4/st…

在当时的约束文档中也体现了只有这2种类型

<!--================== HTML content models ===============================-->

<!--
    HTML has two basic content models:

        %inline;     character level elements and text strings
        %block;      block-like elements e.g. paragraphs and lists
-->

www.w3.org/TR/html4/st…

这里有一个很特殊的标签, 也就是我们的a标签, 官方的DTD约束中定义 a 标签只允许inline元素

<!--================== The Anchor Element ================================-->

<!ENTITY % Shape "(rect|circle|poly|default)">
<!ENTITY % Coords "CDATA" -- comma-separated list of lengths -->

<!ELEMENT A - - (%inline;)* -(A)       -- anchor -->
<!ATTLIST A
%attrs;                              -- %coreattrs, %i18n, %events --
charset     %Charset;      #IMPLIED  -- char encoding of linked resource --
type        %ContentType;  #IMPLIED  -- advisory content type --
name        CDATA          #IMPLIED  -- named link end --
href        %URI;          #IMPLIED  -- URI for linked resource --
hreflang    %LanguageCode; #IMPLIED  -- language code --
rel         %LinkTypes;    #IMPLIED  -- forward link types --
rev         %LinkTypes;    #IMPLIED  -- reverse link types --
...
>

定义了一个 a 标签, - -表示开始标签和结束标签都是必需的, (%inline;)*表示嵌套的元素为inline级别, 可以有 0个 或者 无数个, -(A)表示不允许嵌套 A 标签, 其次a标签在早期的这种约束中, 只允许包含 inline元素, 不允许包含其他的元素.

所以在标准规范上而言, 我们的a标签只能嵌套文本, 不过浏览器给我们做了很多的兼容, 使得我们的a标签, 几乎可以嵌套所有的元素. 这个问题在HTML5的规范中, 进行了解决.

拓展:

html4版本的 文档声明

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
     "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
   <HEAD>
      <TITLE>My first HTML document</TITLE>
   </HEAD>
   <BODY>
      <P>Hello world!
   </BODY>
</HTML>

如何阅读DTD: www.w3.org/TR/html401/…

HTML5对元素的分类

元素分类

HTML5中对元素有着更细力度的分类.

基于内容分类:

html.spec.whatwg.org/multipage/d…

中文版: htmlspecs.com/#kinds-of-c…

Each element defined in this specification has a content model: a description of the element's expected contents

定义的每个元素都有一个内容模型: 从而描述该元素预期内容

Each element in HTML falls into zero or more categories that group elements with similar characteristics together.

而HTML中的每个元素属于以下的一个或多个类别

新版的html对于元素的分类更加清晰的定义了元素可以包含的内容.

而所谓的内容模型我个人觉得就是 规范性描述了子元素和后代元素包含的内容

www.w3.org/TR/html401/…

在现在的文档中, 已经不需要通过<!DOCTYPE> 引入DTD约束了. 浏览器默认使用标准对html文件进行解析

www.w3.org/TR/html5-di…

新版的元素描述中, 对于a标签的分类是

归于Transparent类型, 但是不能是可交互内容的子元素

对于 Transparent 的类型说明

Some elements are described as transparent; they have "transparent" in the description of their content model. The content model of a transparent element is derived from the content model of its parent element: the elements required in the part of the content model that is "transparent" are the same elements as required in the part of the content model of the parent of the transparent element in which the transparent element finds itself.

有些元素被描述为透明;它们的内容模型描述中包含“透明”一词。一个透明元素的内容模型源自其父元素的内容模型:在透明部分所需的元素与透明元素所在的父元素内容模型中所需的元素相同

所以 a 标签, 嵌套元素就相当于看其父标签能否嵌套这些元素.

至于 no interactive content(不可交互内容) 规范中对于可交互元素有如下这些

Interactive content is content that is specifically intended for user interaction.

a (if the href attribute is present) audio (if the controls attribute is present) button details embed iframe img (if the usemap attribute is present) input (if the type attribute is not in the Hidden state) label select textarea video (if the controls attribute is present) 翻译 a(如果存在href属性) audio(如果存在controls属性) button details embed iframe img(如果存在usemap属性) input(如果type属性不处于隐藏状态) label select textarea video(如果存在controls属性)

所以这样的嵌套是合法的

<div>
  <a>
    <div>aaa</div>
  </a>
</div>

这样的是不合法的, 因为button是可交互元素.

<div>
  <a>
    <button>
      你点我呀
    </button>
  </a>
</div>

但浏览器做了优化, 允许这样的嵌套, 如果去w3c中检测, 会出现如下问题

这样也是不合法的

<p>
  <a>
    <div>aaa</div>
  </a>
</p>

被浏览器解析成这样

因为:

常见的元素的嵌套关系

div

span

span是可以包含 p 标签, 但是不能包含div

这是合法的.

<span>
  <p>你好啊</p>
</span>

ul

总结

以后遇到了就可以直接查询了, 大部分情况下, 我们依旧可以按html4中说的那样进行记忆, 毕竟我们需要向后兼容

  1. div几乎可以包含所有元素
  2. 块级可以包含行级元素
  3. 行级不一定不能包含块级元素

元素水平居中

margin: auto的计算

需要注意, 虽然我们知道在css2.2的 visual formatting model中依旧是将元素作为 block, inline进行处理, 通过生成 container box 来进行布局, 但是有些元素可能会被 一些 内容 替换, 所以针对 block, inline有着更多的种类.

对于这些可替换, 或者不可替换的元素, 可能 margin: auto的效果不同.

其次对于设置为float, 或者 position 的元素也会有其他的计算方式

对于这些经常看见的术语定义, 可以参考: www.w3.org/TR/css-disp…

namedesc中文
replaced elementAn element whose content is outside the scope of the CSS formatting model, such as an image or embedded document. For example, the content of the HTML img element is often replaced by the image that its src attribute designates.Replaced elements often have natural dimensions. For example, a bitmap image has a natural width and a natural height specified in absolute units (from which the natural ratio can obviously be determined). On the other hand, other objects may not have any natural dimensions (for example, a blank HTML document). See [css-images-3].User agents may consider a replaced element to not have any natural dimensions if it is believed that those dimensions could leak sensitive information to a third party. For example, if an HTML document changed natural size depending on the user’s bank balance, then the UA might want to act as if that resource had no natural dimensions.The content of replaced elements is not considered in the CSS formatting model; however, their natural dimensions are used in various layout calculations. Replaced elements always establish an independent formatting context.一个内容不在CSS格式化模型范围内的元素, 如图像或嵌入文档。 例如,HTML中 img元素的内容 通常由其 src属性指定的图像替代。替换元素通常具有自然尺寸。 例如,位图图像有一个以绝对单位指定的自然宽度和自然高度 (可以由此显然确定自然比例)。 另一方面,其他对象可能没有任何自然尺寸 (例如,一个空白的HTML文档)。 参见[css-images-3]。用户代理可能会认为替换元素没有任何自然尺寸, 如果认为这些尺寸可能会向第三方泄露敏感信息。 例如,如果HTML文档的自然大小根据用户的银行余额变化, 那么UA可能希望将该资源视为没有自然尺寸。替换元素的内容不被认为属于CSS格式化模型; 然而,它们的自然尺寸会在各种布局计算中使用。 替换元素总是会建立一个独立的格式化上下文
non-replacedone that is not replaced, i.e. whose rendering is dictated by the CSS model.没有被替换的元素, 即其渲染由CSS模型决定

对于常规开发来说, 我们所看见的 可替换元素, 就是 img

为何下面的代码只能是水平居中

margin: 0 auto

来源: www.w3.org/TR/CSS2/vis…

在CSS的VFM中有着明确的定义

inline, no-replaced element 内联,非替换元素

The 'width' property does not apply. A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.

'width' 属性不适用。对于 'margin-left' 或 'margin-right' 计算值为 'auto' 的情况,实际值为 '0' 这里的计算值, 实际值. 额, 它又涉及到我们的CSS文件解析的过程, 我们有机会再整理一篇相关的博客. 其实HTML有DOM. CSS也有CSSOM (^-^)

Inline, replaced elements 内联, 可替换元素

A computed value of 'auto' for 'margin-left' or 'margin-right' becomes a used value of '0'.

对于 'margin-left' 或 'margin-right' 计算值为 'auto' 的情况,实际值为 '0'。

Block-level, non-replaced elements in normal flow 块级, 在文档流中的非替换元素

If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element with respect to the edges of the containing block.

如果 'margin-left' 和 'margin-right' 都是 'auto',它们的实际值相等。这会使元素相对于包含块的边缘水平居中。

Block-level, replaced elements in normal flow 块级, 在文档流的可替换元素

The used value of 'width' is determined as for inline replaced elements. Then the rules for non-replaced block-level elements are applied to determine the margins.

对于替换元素,'width' 的使用值的确定方式与 行内替换元素 相同。 然后应用 块级非替换元素 的规则来确定边距。也就是会水平居中. 注意, 是需要确定元素的width情况下

所以注意这个代码, 如果设置为block可以居中, 但是设置为inline-block就不可以了. 因为inline-block的p元素属于inline-level, no-replace元素, 所以margin: 'auto' 情况下, 得到的实际值为0

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>margin控制居中</title>
    <style>
      p {
        width: 40vw;
        margin: 0 auto;
        /* display: block; */
        display: inline-block;
        background-color: cornflowerblue;
        vertical-align: middle;
      }
    </style>
  </head>
  <body>
    <div>
      <p>好久不见, 甚是想念, 近来可好!</p>
    </div>
  </body>
</html>

下面的这个代码就会使得img居中, 通过display: block使得img成为了 块级, 在文档流中的替换元素

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>margin控制居中</title>
    <style>
      .small-img {
        display: block;
        margin: 0 auto;
        width: 40vw;
      }
    </style>
  </head>
  <body>
    <div>
      <img
        class="small-img"
        src="https://lf-web-assets.juejin.cn/obj/juejin-web/xitu_juejin_web/e08da34488b114bd4c665ba2fa520a31.svg"
      />
    </div>
  </body>
</html>

基于text-align实现居中

这个属性名很具有欺骗性. text在常规意义上指代的是文本.

在w3c中: drafts.csswg.org/css-text/#t…

Inline-level content is centered within the line box. 内联级内容在行盒中居中.

所以对于inline-block的元素, 他们的外在表现是inline-level的. 可以通过text-align进行居中

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>margin控制居中</title>
    <style>
      div {
        text-align: center;
      }
      p {
        width: 40vw;
        margin: 0 auto;
        display: inline-block;
        background-color: cornflowerblue;
        vertical-align: middle;
      }
    </style>
  </head>
  <body>
    <div>
      <p>好久不见, 甚是想念, 近来可好!</p>
    </div>
  </body>
</html>