【CSS】【译】一文了解 CSS 中关于 auto 的一切

3,062 阅读11分钟

翻译自英文原文: Everything About Auto in CSS, 已获得授权。

在 CSS 中,我们可以在 margin, positioning, height, width 等等属性中使用 auto 这个值。我会在这篇文章中记录我所知道的所有关于 auto 的知识,希望能够帮助到更多人,尤其是想深入了解 auto 相关知识的人。

接下来,我首先会解释一下 auto 的技术细节以及我们如何来最大程度的利用它。当然,还会有一些使用案例和示例代码。

介绍

auto 关键字的使用因属性而异,接下来我会依次介绍在不同属性中的使用 auto 的情况。

Width: Auto

块级元素如 <div>, <p>width 属性的初始值就是 auto, 意味着它们将占据其父元素的所有水平空间。

根据 CSS 的规范:

‘margin-left’ + ‘border-left-width’ + ‘padding-left’ + ‘width’ + ‘padding-right’ + ‘border-right-width’ + ‘margin-right’ = width of containing block

当一个元素的宽度被设置成 auto 时, 即使设置了 marginpadding, border 属性,也不会超出其父元素的水平空间。其内容框的宽度将是内容本身,减去 marginpaddingborder

以上面的模型为例:

  • HTML
<div class="wrapper">
  <div class="item"></div>
</div>
  • CSS
* {
    box-sizing: border-box;
}

.wrapper {
      max-width: 600px;
      margin: 2rem auto 0;
      padding: 1rem;
}

.item {
      padding: 1rem;
      margin: 0 50px;
      border: 15px solid #1f2e17;
}

一切都很完美。文本内容被限制在其父元素的空间里面。

但是,如果我们把元素的 width 的值从 auto 改成 100% 会怎么样呢?

结果就是,这个元素就会占用其父元素宽度的 100%加上左右两侧的 margin 值。

  • CSS
.item {
      width: 100%;
      padding: 1rem;
      margin: 0 50px;
      border: 15px solid #1f2e17;
}

此时这个元素的宽度就是 568px, 计算方法如下:

‘border-left-width’ + ‘padding-left’ + ‘width’ + ‘padding-right’ + ‘border-right-width’

15 + 16 + 506 + 16 + 15 = 568px

如果文字方向是 ltr, 那么 margin-right 的值会被浏览器忽略。在这个例子中就可以看到效果。相应的,如果文字方向是 rtl , 那么 margin-left 的值就会被忽略。

你可以点击 这里 查看示例代码

Width: Auto 的使用案例

只是解释基本原理还远不能让我们真正掌握这个概念。所以下面是我准备的几个关于 width: auto 实用的使用案例。

在移动端和桌面端展示不同的宽度

假设我们有一个按钮组。在移动端上,我们想让这两个按钮显示在同一行且每个按钮占其父元素宽度的 50%。在桌面端时,每一个按钮都要占其父元素宽度的全部空间。怎么实现这种效果呢?

  • HTML
<div class="group">
    <div class="group__item">
        <button class="c-button">Sign In</button>
    </div>
    <div class="group__item">
        <button class="c-button c-button--ghost">Register</button>
    </div>
</div>

在这里,可以用 flexbox 来使得这两个按钮相邻排放。

  • CSS
.group {
    display: flex;
}

.group__item {
    width: 50%;
}

对于桌面端来说,我需要每一个按钮都占用全部的父元素宽度,你可能会想尝试用 100% 来实现对吧?看看下面这个方法,比 100% 更好哦。

  • CSS
@media (min-width: 800px) {
    /* Revert the wrapper to a block element instead of flex */
    .group {
        display: block;
    }

    .group__item {
        width: auto;
    }
}

因为 .group__item 是一个块级元素, 使用 width: auto 就可以让它完美的占满父元素的全部可用空间。

你可以点击 这里 看最终效果。

Height: Auto

对于 height 这个属性来说, 和 width 的情况完全不同。一个元素的高度等于它实际内容占用的高度,默认值就是 auto

来看下面这个例子。

  • HTML
<div class="wrapper">
  <div class="item">What's my height?</div>
</div>

为了让 .item 元素占用其父元素的全部高度, 我们可以采用下面的任一方法来实现:

  1. .wrapper 一个固定的高度, 然后给 .item 设置 height: 100%
  2. .wrapper 设置成 display: flex, 它就会默认拉伸他的子元素 .item
  • CSS
.wrapper {
    height: 200px;
}

.item {
    height: 100%;
}

Margin: Auto

对于 margin 属性来说, 最常见的用例是将具有已知宽度的元素水平居中。

以下面的例子为例:

如果要把蓝色的矩形放到水平方向的中心位置, 我们需要使用下面的CSS:

  • CSS
.element {
    margin-left: auto;
    margin-right: auto;
}

也可参考CSS 规范中关于 margin: auto 的描述:

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-leftmargin-right 的值都是 auto, 那么这两个的使用的真实值就是相等的,也会使得元素相对于父元素的边缘水平居中。

你可以点击 这里 查看效果

在绝对定位的元素上使用 margin:auto

另一个不太常见的例子就是利用 margin: auto 把一个绝对定位的元素居中。

前提条件:

  1. 这个元素的必须有一个固定的宽度和高度
  2. 这个元素必须是绝对定位 position: absolute
  • HTML
<div class="wrapper">
  <div class="item">I am centered.</div>
</div>
  • CSS
.wrapper {
    position: relative;
}

.item {
    width: 200px;
    height: 100px;
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}

Flexbox

flex 布局的父元素内,给子元素的 margin 设置为 auto 会让这个子元素被 "推到" 对应的另一边,这在有些应用场景下是非常有用的。比如, 如果一个 flex 布局的元素设置成 margin-left: auto, 那么它就会被 “推到” 最右边。

看下面这个例子,父元素是一个 flex 布局的容器,在容器内有两个矩形元素。

我们想让 2 号矩形放置在最右边,此时使用 margin-left: auto就再合适不过了。

  • CSS
.wrapper {
    display: flex;
}

.item-2 {
    margin-left: auto;
}

垂直方向同理:

  • CSS
.item-2 {
    margin-top: auto;
}

特别的,如果容器内只有一个子元素,我们可以用 margin: auto 来将其水平以及垂直居中。

  • CSS
.item-1 {
    margin: auto;
}

flex: auto

在 flex 布局中,如果给一个子元素设置了 flex: auto, 就相当于设置了 flex: 1 1 auto,即:

  • CSS
    .item {
        flex-grow: 1;
        flex-shrink: 1;
        flex-basis: auto;
    }

根据 MDN的规范, 设置了flex:auto的元素将根据其宽度和高度来调整大小,但它可以根据可用的额外空间来增大或缩小。

  • HTML
<div class="wrapper">
  <div class="item item-1">Item</div>
  <div class="item">Item</div>
  <div class="item">Item</div>
</div>
  • CSS
.wrapper {
    display: flex;
    flex-wrap: wrap;
}

.item {
    width: 120px;
    height: 500px;
}

.item-1 {
    flex: auto;
}

你可以点击 这里 查看在线示例。

Grid 布局下使用 Auto

给某一个列设置 auto

在 Grid 布局中,我们可以给某一列设置为 auto之后,这一列的宽度将取决于包含在其中的内容的长度。

  • CSS
.wrapper {
    display: grid;
    grid-template-columns: auto 1fr 1fr;
}

给某一个元素设置 margin:auto

在 Grid 布局中,给元素设置 margin:auto 可以达到和 Flex 布局相似的效果。

这是如果给其中一个 Grid 布局的元素设置了 margin-left: auto, 那么它将被 “推到” 右侧,同时它的宽度将取决于内容的宽度,而不是 Grid 的宽度了。

  • CSS
    .item-1 {
        margin-left: auto;
    }

Overflow

当我们创建一个元素时,应该考虑到放置其中的最小内容以及最大内容。如果实际的内容超出了设置的容器大小,就需要展示一个滚动条:

  • CSS
.element {
    overflow-y: scroll;
}

但是这样的话,即使内容很少滚动条也可能会显示,如下图所示:

尤其是在 Windows 平台下的 Chrome 浏览器上,滚动条一直都是显示的。

这时候我们应该用 scroll-y: auto来保证只有内容超过了容器大小的时候 滚动条才会出现。

  • CSS
.element {
    overflow-y: auto;
}

定位属性: top, right, bottom, left

对于CSS定位属性top,right,bottom和left,我们可以使用auto关键字作为它们的值。

以下面的布局为例:

我们有一个设置了 padding 的父容器,其中只有一个子元素。其中的子元素是绝对定位且没有设置任何定位属性。

  • CSS
.wrapper {
    position: relative;
    padding: 16px;
}

.item {
    position: absolute;
    width: 100px;
    height: 100px;
}

我们知道,在CSS中,每一个属性都有一个初始值或者默认值。如果这个时候我在浏览器上审查这个子元素,查看计算出来的样式,猜一下它的 left 属性的值是多少?

答案是: 16px!

明明没有设置过这个属性,为什么 left 的值会是 16px ?

原因就是一个绝对定位的元素是相对于离其最近的相对定位的父元素来定位的。在这个例子中,父元素的padding 属性是16px, 所以子元素就离父元素的上面和左边各16px。很有意思,不是吗?

现在你可能会问,知道了这个有什么好处?别急,继续往下看。

让我们假设,我们想让这个子元素在小屏幕上显示时距离父元素左边100px,而在桌面端(大屏幕)上显示时回到其原来的位置。

我们可以使用绝对定位来实现距离左边100px:

.wrapper {
    position: relative;
}

.item {
    position: absolute;
    left: 100px;
    width: 100px;
    height: 100px;
}

现在的问题就是: 如何让元素回到原来的位置?

我们不能使用 left: 0, 这样的话元素就会距离边缘 0px,如下图所示:

这时候我们就可以用 left:auto, 根据 MDN 的描述:

The element is positioned where it should horizontally be positioned if it were a static element.

意思就是, left:auto 将使得元素定位时遵循父元素的 padding 属性,而不是遵循父元素的边界。

最终的 CSS 如下:

.item {
    position: absolute;
    left: 100px;
    width: 100px;
    height: 100px;
}

@media (min-width: 800px) {
    .item {
        /* This is equivalent to left: 16px */
        left: auto;
    }
}

top 属性同理。

对于 right 和 bottom 属性,他们的默认计算出来的值分别等于元素的宽度和高度。

你可以点击 这里 查看在线示例。

使用场景和示例

下面我会展示一些关于 auto 使用的应用场景和示例,希望能帮到你。

Tooltip 的箭头

我们在设计 Tooltip 时需要考虑到多种情况,比如有的箭头在左边,有的箭头在右边:

  • CSS
.tooltip:before {
    /* Arrow code */
    position: absolute;
    left: -15px;
}

/* This is a version where the arrow is pointing to the right */
.tooltip.to-right:before {
    /* Arrow code */
    position: absolute;
    left: auto;
    right: -15px;
}

需要注意的是,我在上面的这一版的实现里面使用了 left:auto 来覆盖了 left: -15px. 但是我更推荐用下面的写法来代替 left:-15px

  • CSS
.tooltip:before {
    position: absolute;
    right: 100%;
}

.tooltip.to-right:before {
    /* Arrow code */
    position: absolute;
    right: auto;
    left: 100%;
}

看到区别了吗?我们用100%代替了 -15px 这种将尺寸写死在 CSS 样式中的方式。这才是一个面向未来的解决方案.

卡片组件

一个卡片组件一般都会在左上角有一个图片 / 按钮,可以是为了美观或是为了添加一个有用的按钮。不过不管其作用是什么,我们都要使这个图片 / 按钮可以会出现在不同方向上。即左上角和右上角:

我们通过给 .card .icon 设置 left: auto ,就可以很方便的将其放置在卡片的右侧了。

  • CSS
.card .icon {
    position: absolute;
    left: 15px;
    top: 15px;
}

.card.is-right .icon {
    left: auto;
    right: 15px;
}

Flex 布局中使用 margin: auto

在 Flex 布局中,没有什么是不可能的。当我们把 Flex 布局和 margin:auto 结合在一起使用时就可以做出很多实用的布局。

以下图这个布局效果为例,每一行都包含一个标题,描述和一个动作按钮,而且动作按钮要始终置于每一行的右侧位置。

我们就可以通过给动作按钮设置 margin-left:auto来实现:

  • HTML
<div class="item">
    <div class="item-group">
        <!-- Title and description -->
    </div>
    <button class="item__action">Confirm</button>
</div>
  • CSS
    .item {
        display: flex;
        flex-wrap: wrap;
        justify-content: space-between;
    }

    .item__action {
        margin-left: auto;
    }

CSS Grid 布局中使用 margin:auto

Grid 布局里的元素, 它们的 margin 属性可以设置成固定值,百分比或者是 auto, 下面是一个通过给元素的 margin 属性设置 auto 来实现标签元素从左对齐变为右对齐的例子:

  • 左对齐效果

  • HTML
<p class="input-group">
    <label for="">Full Name</label>
    <input type="email" name="" id="">
</p>
  • CSS
.input-group {
  display: grid;
  grid-template-columns: 1fr;
  grid-gap: 1rem;

  @media (min-width: 700px) {
    grid-template-columns: 0.7fr 2fr;
  }
}

给 label 添加 margin-left: auto 属性之后:

固定高度的模态框

设计模态框时要处理好一个重要的问题: 如果模态框中的内容高度特别大怎么办?

这种情况下,我们可以用下面的样式:

.modal-body {
    overflow-y: auto;
}

这样如果内容高度超出模态框的容器大小时,就会自动显示一个纵向的滚动条了。