淘先锋技术网

首页 1 2 3 4 5 6 7

1. 摘要

一开始只是在阅读Material UI的Grid布局文档, 发现了官网中关于Grid布局的局限中的这句:

Negative margin

The spacing between items is implemented with a negative margin. This might lead to unexpected behaviors. For instance, to apply a background color, you need to apply display: flex; to the parent.

于是乎查看Grid布局中item spacing的实现细节,发现它是通过将margin设置为负数以及item的父元素设置padding的方式实现spacing,然后对比Antd,发现后者是通过row-gap属性实现item spacing。期间又发现了基础知识:外边距合并

2. Material UI 中 Grid 布局的 spacing 细节

截图

图1

Grid Spacing 图

图2

在这里插入图片描述

图3

在这里插入图片描述
图1中便显示了Grid的容器元素设置了margin-top和margin-left为负数,它之所以要设置为负数,是因为Item元素在实现spacing时,是通过设置padding-top和padding-left来让item的content实现spacing效果的,也就是说item之间本身就是挨在一块的,通过下图中将padding-left属性取消了就能看到效果。
在这里插入图片描述也就是因为item使用了padding-top和padding-left的缘故,其content元素内容的位置分别向下和向右偏移了。
注意要区分item和item的content,我们在写代码时设置的是content。
为了纠正向下和向右的偏移,在容器元素上就需要设置负数margin-top和margin-left,把content的位置拉回其应该所在的位置
默认情况下,flex布局中相关的属性中并不包含直接的设置item间距的属性,通常情况下能改变item间距是justify-content属性,但它并不支持自定义设置间距。所以才有了material UI设置spacing功能。
这里所说的应该所在的位置可以这么理解:假设只有一个item,它应该在中间位置,当我们启用spacing功能时,如果容器元素不设置负数的margin,那么item的content就会因为padding不在中间位置,而是发生偏移。

3. Antd 中使用的 row-gap 属性

在这里插入图片描述
可以看到容器元素使用了row-gap属性,但是它依旧设置了负数的margin-left和margin-right。
其原因在于,row-gap属性是用来控制不同行之间的间距,而同一行的间距则是依旧是通过设置padding来实现,然后就是偏移和解决偏移的思路。

row-gap属性兼容性

在这里插入图片描述

emmmmm,估计本来是用于grid,后来支持了flex,而且在safari上还不支持,不知道antd 在Safari上怎么实现的。

margin值为负数的影响

在这里插入图片描述
如上图,能够明显看出来,下面的Grid容器由于使用负数margin,当设置背景色为红色时能够发现,整个container部分覆盖了前一个兄弟元素。因为负数margin会使得整个元素位置向上移动。在官方文档中

apply display: flex; to the parent.

但是没仔细说是谁的parent,如果说是container和兄弟元素共同的parent,设置display:flex就会改变其他元素布局,这跟组件使用的原则相冲突也不太合理。因此只能认为是给container加一个parent并设置display:flex。但是接下来将测试加display:flex和不加的区别。

父元素不设置display:flex

在这里插入图片描述
能够发现父元素没添加display:flex时,依旧还是覆盖了部分兄弟元素。这里涉及到的便是外边距合并,简单来说就是container的父元素只有container这一个子元素,父元素的margin和container的margin相邻便产生了外边距合并,作为子元素的container的负数margin也影响了父元素的位置。

父元素设置display:flex

在这里插入图片描述
在这里插入图片描述
虽然父元素依旧部分覆盖了兄弟元素,但是父元素的高度已经比不添加display:flex来说回归正常。当我们再设置overflow:auto,如下图
在这里插入图片描述
便能使得整个container的background color显示正常。其实即使不设置display:flex,也能实现同样的效果。

外边距合并,块级格式化上下文和负数margin值

上述问题中,起因便是负数margin值会改变元素位置,它似乎没有脱离文档流,但是位置依旧被改变了,因此在设置背景色或其他属性可能会影响到其他元素。
而当添加父元素,不做其他设置依旧会覆盖部分兄弟元素便是因为外边距合并,关于外边距合并,知乎的[外边距合并和实践]说的较为详细(https://zhuanlan.zhihu.com/p/56467985),引用其一些话:

产生外边距合并的必备条件:margin必须是邻接的!

而根据w3c规范,两个margin是邻接的必须满足以下条件:

  • 必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC当中。
  • 没有padding和border将他们分隔开
  • 都属于垂直方向上相邻的外边距,可以是下面任意一种情况
  • 元素的margin-top与其第一个常规文档流的子元素的margin-top
  • 元素的margin-bottom与其下一个常规文档流的兄弟元素的margin-top
  • height为auto的元素的margin-bottom与其最后一个常规文档流的子元素的margin-bottom
  • 高度为0并且最小高度也为0,不包含常规文档流的子元素,并且自身没有建立新的BFC的元素的margin-top和margin-bottom

以上的条件意味着下列的规则:

  • 创建了新的BFC的元素(浮动,绝对定位,以及各种产生BFC的操作),与它的子元素的外边距不会折叠
  • inline-block元素不与任何元素的外边距产生折叠(不是块元素)
  • 一个常规文档流元素的margin-bottom与它下一个常规文档流的兄弟元素的margin-top会产生折叠,除非它们之间存在间隙(clearance)。
  • 一个常规文档流元素的margin-top 与其第一个常规文档流的子元素的margin-top产生折叠,条件为父元素不包含 padding 和 border ,子元素不包含 clearance。
  • 一个 ‘height’ 为 ‘auto’ 并且 ‘min-height’ 为 '0’的常规文档流元素的 margin-bottom 会与其最后一个常规文档流子元素的 margin-bottom 折叠,条件为父元素不包含 padding 和 border ,子元素的 margin-bottom 不与包含 clearance 的 margin-top 折叠。
  • 一个不包含border-top、border-bottom、padding-top、padding-bottom的常规文档流元素,并且其 ‘height’ 为 0 或 ‘auto’, ‘min-height’ 为 ‘0’,其里面也不包含行盒(line box),其自身的 margin-top 和 margin-bottom 会折叠。

因此为了不产生外边距合并,可以设置父元素的padding和border,也可以通过为父元素变成一个新的BFC的方法,创建一个独立的渲染区域。

PS
其实感觉必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个**BFC**当中这句话有点问题,比如上面父元素处于默认的BFC中,而container是自己的BFC,不处于同一个BFC中,这种情况下依旧产生了外边距合并。也有可能这句话是针对非父子元素关系的

块级格式上下文的特点

  1. 清除浮动:当一个元素内部的子元素都浮动,而该元素没有设置高度,就会导致该元素高度为0,不能正常显示。此时可以将该元素触发BFC,以清除浮动并自适应高度。
  2. 防止外边距合并:在普通流中,两个相邻元素的垂直方向的外边距会发生合并,导致间距变小。但是,如果这两个元素分别处于不同的BFC中,它们的外边距就不会合并。
  3. 包含浮动元素:当一个元素包含浮动元素时,该元素的高度无法被浮动元素撑起来,导致出现布局问题。通过创建BFC,可以使该元素包含浮动元素并正常显示。
  4. 防止文本环绕:当一个元素旁边有浮动元素时,文本可能会环绕在浮动元素周围。通过将该元素触发BFC,可以防止文本环绕。

如何创建块级格式上下文

  1. 浮动元素,float 除 none 以外的值
  2. 定位元素,position的值不是static或者relative。
  3. display 为 inline-block 、table-cell、table-caption、table、table-row、table-row-group、table-header-group、table-footer-group、inline-table、flow-root、flex或 inline-flex、grid或 inline-grid
  4. overflow 除了 visible 以外的值(hidden,auto,scroll)
  5. 根元素 就是一个 BFC

浮动的影响

在以下代码中:

<div class='parent'>
   <div class='child1'></div>
   <div class='child2'></div>
</div>

当child1设置float:left时,child2的高度和父元素相同是为什么:

当一个元素设置了浮动(float)之后,它会脱离正常的文档流,而其父元素将不再包含该元素。这意味着父元素会折叠到它的内容高度上,并且可能导致布局问题。
在提供的代码中,当child1设置float:left时,它会脱离文档流并向左浮动,而child2没有设置浮动,仍然保持在原来的位置。由于父元素的高度由子元素决定(也就是包含非浮动子元素的高度),因此child2会自动填充剩余空间并与父元素等高。
需要注意的是,当child2中存在文本或其他内联元素时,在某些情况下可能会导致高度计算不准确,因为内联元素的高度可能会超出其容器的高度。此时可以使用clearfix 或者其他方式来清除浮动以避免这种问题。