分享几个 CSS 技巧

February 10, 2021

前言

在实际工作中,为了百分百还原设计师们精致而又严格(变态)的设计稿,我们经常需要借助一些 CSS-Tricks 来保障页面的高还原度,不然设计妹子的像素眼分分钟找出遗漏点,向你发难。

晚痛不如早痛,而我切页面的原则有三:

  • 能单凭 CSS 解决,绝不让 JS 混入其中
  • 能利用优先级覆盖,绝不使用 !important
  • 大胆使用 CSS 新特性、CSS-Tricks(只需兼容 Chrome 大法好)

因此,这边文章分享了关于 CSS 的几个小技巧,可能正解救了处于水生火热的你。

FBI WARNING:本文所有例子均采用 jsfiddle 呈现,需要科学上网。

子元素无视父元素的 overflow: hidden 限制

场景如下,父元素设置了 overflow: hidden,但要求子元素能一部分显示在外,这显然与父元素的 hidden 所矛盾。

换个角度,可以使子元素脱离文档流并不依据父元素定位,即添加 position: absolute,你可以看下面的示例:

但要注意,此时父元素不可设置为 position: relative | absolute | fixed | sticky,要以父元素外层的元素,作为 position: absolute 的基准定位元素。

因为 position: absolute 的定位原则是以 第一个不为 static 定位(默认定位,即初始值) 的元素来进行定位,如果全是 static,则依据根元素 <html> 进行定位。

利用 flex 实现伸缩布局

不多解释,直接上 demo:

这类布局在各 Website、App 会经常看到,大致有两个特点:

  • 容器总高度固定,<header> & <footer> 高度随内容而定
  • <main> 为一个 可滚动区域,其内容会占据所有剩余空间

乍一看好像没什么,但内有玄机,在我以往的认知中,要为一个元素呈现 overflow-y: scroll 的效果,必须显式设置该元素的高度(height)。

而且还要求该元素的高度能动态调整,占满剩余高度,难不成要使用万能的 JS 进行计算?No,这违背了我的原则。

借助于 flex 弹性布局,我们可以很完美得解决这个问题,<main>flex: 1 其实等同于:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 0%

关键在于前面两个属性定义了该 flex 子元素总是会向外增长,即父 flex 容器有剩余空间时,总是会占满。相当于隐式设置了高度,从而能进行滚动。

滚动条不占据内容宽度

继续围绕 overflow-y: scroll 出现滚动条的话题,以 Mac 下的 Chrome 为例,滚动条默认不会占据内容宽度,也不会显示,只有当鼠标在可滚动区域内滚动时,滚动条才会显示。

但当我们主动设置滚动条的样式时,滚动条会默认占据内容的宽度,你可以看下面的 demo 示例:

借助于 overflow-y: overlay 属性,它可以使滚动条不占据内容宽度,就好比设置了 position: fixed 定位一般,固定于右侧。

我设置了盒子的 padding-right: 8px,而滚动条宽度也为 8px,可以看到 "Placeholder" 紧贴着滚动条,说明 overflow-y: overlay 起作用了。

还原样式属性

cursor: pointer 这个属性并不陌生,当光标经过设置该属性的元素时,会变成可点击形态。那么问题来了,如果在该元素的内部,一些子元素不需要可点击的光标形态,该如何设置子元素的 cursor 属性来覆盖父元素的 pointer 行为?

或许你可以去 MDN 上查询 cursor 的所有可选值,并找到默认值。我猜是 default,恰巧 default 确实存在,但其表现还是与默认形态有一定的差异(一直是箭头形态,经过文字时不会变化,比如括号中的文字就是设置了 default 值),经过官方文档的查阅,auto 才是光标的 “正统” 默认值。

上述的方法未免太过繁琐,有没有又快又好的技巧,the answer is YES!

官方文档中明确指出 cursor 的规范定义如下:

Initial value auto
Applies to all elements
Inherited yes

轻松得知两个关键信息:

  • 它是一个可继承属性,因此子元素不显式设置 cursor: pointer 也会显示可点击形态,正是继承了父元素的属性
  • 它的初始值是 cursor: auto

同时对于所有 CSS 属性,都存在三个可选值:

  • initial,该属性使用默认值
  • inherit,该属性继承父元素的值
  • unset,如果该属性是可继承属性,该值等同于 inherit;如果该属性是非继承属性,该值等同于 initial

问题迎刃而解,我无需知道 cursor 的默认值是哪个关键词(auto),只需知道 initial.

width: fit-content | min-content | max-content

以前觉得 width 只是个平平无奇的属性,用来设置的元素的宽度,如果强行增加难度,无非就是:

  • 配合 box-sizing: border-box 设置整个盒子模型的宽度,而非 content-width
  • max-width 和 min-width 属性会覆盖 width 属性

但其实它还有三个关键词可供选择,能快速实现一些布局效果。

fit-content 配合 margin: auto 实现收缩且居中

当我提到水平居中一个元素时,你会想到什么方法?对于不同 display 的元素自然有着不同的方法:

  • display: block,只需添加 margin: auto
  • display: inline-block,需要父元素额外设置 text-align: center

其实,display 可以拆分为两个属性:display-outsidedisplay-inside,它们分别设置一个元素的 “外部排列” 和 “内部排列” 特性。

display: inline-block 为例,它的外部表现是一个行内元素(inline):宽度根据内容拓展,不占据一行,但内部表现是一个块级元素(block):可设置宽高以及竖直方向上的 margin、padding

{
  display: inline-block;
  /* display-outside: inline; */
  /* display-inside: block; */
}

假设,我们既要实现元素 收缩效果 的同时,又要保持原本的元素 外部 block 状态,该如何是好?使用 width: fit-content 可以轻松解决,该属性维持了原先的 block 状态并向内自适应(根据内容显示宽度,不再默认占满一行,和 inline-block 很相似,但本质上有着差距),可以直接配合 margin: auto 实现居中的效果。

min-content 实现最小宽度

当想实现一个元素根据其子元素的最小宽度来自动设置宽度时,这个属性就派上了用场,那么 “最小宽度” 到底指什么?在子元素没有显式声明宽度的前提下,默认规则如下:

  • 对于可替换元素(img、input、textarea...),最小宽度就是可替换元素的宽度,例如图片的宽度
  • 对于中文文本元素,最小宽度值就是一个中文字符的宽度
  • 对于英文文本元素,最小宽度就是其中最长英文单词的宽度

可替换元素,顾名思义,它们拥有默认的宽高,并且可以被设置具体的宽高值

为了方便解释,这边举了三个例子:

max-content 实现不折行

有 min-content,就有 max-content,当一个元素的 width 属性被赋予该值时,他的宽度值会仅可能得大。即假设我们的容器有足够的宽度,足够的空间,此时,所占据最大宽度的子元素的宽度就是 max-content 的值。

在普通的容器中,子元素过长,会自动换行,排列到下一行空间,但 max-content 的存在,就好比设置了 white-space: nowrap,具体表现如 demo 所示:

Reference

理解CSS3 max/min-content及fit-content等width值


Written by B2D1(包邦东)