你所知道的css写法可能并不是最优解

Author: 陌小雨Date: 2023-07-09View: 129

平平无奇的 CSS

CSS 很简单又很复杂,这是大家伙所公认的,如果对于 CSS 的了解仅限于表面的那点属性,一旦遇到一些样式问题,便会束手无策,仅靠不停的去尝试各种其他属性来解决问题,显然这样是不够的,不仅需要对样式属性了解,还需要了解其背后的作用原理,这样写起 CSS 来才会更加得心应手。 那么一起来看看,平时我们可能会写出来的 CSS 代码。

块级元素

块级元素这个大家都很熟了,想想下面这样的代码你有没有写过,反正笔者自己以前是经常会写这样的代码:

css

.demo1 {
width: 300px;
height: 200px;
}
.demo1-child {
width: 100%;
height: 100%;
}

乍一看好像没什么问题,实际就是块级元素的宽度默认就是填满父元素,所以这里的width: 100%根本没有必要。
此外width: 100%是相对于 context-box,也就是说,如果子元素添加 padding ,那么子元素的 100\% + padding 就会超出父元素,反而变得不够灵活。

css

.demo1 {
width: 300px;
height: 200px;
background-color: lightblue;
}
.demo1-child {
width: 100%;
height: 100%;
padding: 20px;
border: 1px solid red;
}

子元素已经超出了父元素,这个时候再想要适应父元素,只能使用box-sizing了,维护起来也变得更麻烦,所以width: 100%这种限制宽度的写法尽量不要。 注意这里height:100%是不能丢弃的,否则 child 元素将无法充满整个父级元素。那为什么宽度就能自动充满,高度就不行呢?这其实和 CSS 的书写显示方向有关,默认情况下,CSS 的书写方向从左到右,从上到下,因此水平方向会自动充满,如果改变 wirting-mode: vertical-lr 则默认块级元素 height 属性会填满父元素。

绝对定位

绝对定位也是大家经常用到的属性,下面的代码也是大家可能会经常遇到的: 代码:

css

.demo2 {
width: 300px;
height: 200px;
position: relative;
border: 1px solid red;
}
.demo2-child {
position: absolute;
top: 0;
left: 0;
background-color: lightpink;
width: 100%;
height: 100%;
}

这么写,也能达到充满父级元素的目的,但还是说明了你对绝对定位了解的不够多。 首先绝对定位元素,不一定需要父级设置相对定位,当元素的父级及其祖先元素的 position 属性都是 static 的话,其包含块(定位元素的位置的参考元素)则相对于根元素定位,也就是 html 元素。
仔细观察上面的结果,可以发现如下几个结论:

如果绝对定位元素的父级或者祖先元素的 position 属性都是默认值的时候,并且存在位置属性left,top,right,bottom这些,其相对于根元素定位
如果父级及祖先元素没有设置定位属性,并且也没有位置属性left.top,right,bottom,则会相对于当前父级元素定位,如果设置了宽高属性,则是可以超出父级元素的。这就是无依赖的绝对定位。

个人认为无依赖的绝对定位,使用场景并不多,复杂布局下,很难出现父级到祖先元素都不会设置定位的情况。 回到一开始的代码,这里有什么问题呢?如果要充满父级,那么 left, top属性自然是没有必要的。另外需要知道的是,如果位置属性设置了对应方向的属性,举例来说,就是同时设置了left, right或者top, bottom属性,绝对定位元素则会填充对应的水平或者垂直方向的内容。
改造以后,CSS 代码变成这样:

css

.demo2-child {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

这么写,还有一个好处,就是完全自适应父级元素,绝对定位元素此时无论是添加 padding,border,margin 都能完美适应。
相当于设置了box-sizing,完美兼容低版本的浏览器,显然更加实用。

内联元素

内联元素绝对是 CSS 当中细节非常多的一个概念了,平时用的非常多的一个场景就是,文本前后的 icon 可以垂直居中,大部分的时候,我们都是这么写的:

css

.demo3 {
display: flex;
align-items: center;
}
.demo3-icon {
display: inline-block;
width: 20px;
height: 20px;
background: url(./delete.png) no-repeat center;
}

html


删除

简单粗暴,确实能达到效果,但是对于整体布局而言并不好,flex 布局会导致 demo3 变成弹性盒子,它里面如果还要做点其他的事,都会受到 flex 的影响,并且 flex 本身也是用来做布局的,文本这种内联的样式,还是使用内联的样式去做。 这个时候。了解的更多的小伙伴就会这么写了:

css

.demo3 {
line-height: 20px;
}
.demo3-icon {
display: inline-block;
width: 20px;
height: 20px;
background: url(./delete.png) no-repeat center;
vertical-align: middle;
}

flex
vertical-align 可以看到虽然写的是 middle 但是并没有真正的 middle,原因是因为不同字体基线不同,基线就是字符x的下边缘,但是会受到不同的字体,以及字体大小影响,常用的微软雅黑字体,其基线会更偏下一点,而对于替换元素来说,并不存在文本,所以其基线就是替换元素的下边缘,对于 inline-block元素也是适用的,不同的地方在于,我们可以利用inline-block的伪元素,往里面写入一个看不见的文本,这样就单纯利用line-height(line-height为什么可以垂直居中,看这里)就可以实现垂直居中了。

css

.demo3 {
line-height: 50px;
}
.demo3-icon::before {
content: '\3000';
}
.demo3-icon {
display: inline-block;
width: 20px;
height: 20px;
background: url(./delete.png) no-repeat center;
}

这里是将字体变大,可以看到依然可以居中,后面是删除了伪元素,垂直居中就被破坏了。content: '\3000'表示一个全角空格,起到一个替代字符的作用。

选择符

选择符就是 CSS 选择器中,例如空格是后代选择符、“>”是子代选择符,“+”是相邻元素选择符,“\~”是随后兄弟选择符,前两个我们用的比较多,后面可能会用的比较少,善用这些选择符,有时候效果可能会更好。 比如经常会遇到的一个场景:一行列表,每个元素之间存在间隔,最后一个元素则不需要这个元素,一般都会这么写:

css

.demo4-list:not(:first-child) {
padding: 0 10px;
border-left: 1px solid;
}

html

列表1
列表2
列表3
列表4

OK,效果没有问题,但是如果我们需要往 HTML 中添加一个元素:

html

列表内容
列表1
列表2
列表3
列表4

结果就不是我们想要的了,此时就需要修改 CSS 代码,比如可以把 first-child 改成 last-child,再把 border-left 改为 border-right 也能解决这个问题。 对于这种标签强相关的情况,就可以考虑采用相邻选择符,比如上面的 CSS 代码改成这样:

css

.demo4-list + .demo4-list {
padding: 0 10px;
border-left: 1px solid;
}

HTML 结构如何新增或者是删除,都不会影响我们原先的样式,显然这么做样式会更加稳定。 ~随后兄弟选择符,和相邻选择符不一样的地方在于,~选择符可以选择相邻的所有兄弟元素,无论是不是同一类型的 HTML 元素,都可以选中,如果它们的类名不同,但是你又希望能够选中它们,则可以考虑使用。

弹性布局的 flex 属性

弹性布局应该是项目中使用的绝大多数的 CSS 属性了,但是可能大部分都会用在诸如水平居中、垂直居中这样的布局上,关于其 flex 属性反而是用的比较少,至少对笔者来说,flex 属性用的比较多的也就 flex: 1 这样的等分弹性容器的属性,现在就来好好说说这个属性。

css

/** 平时用的比较多的 flex 属性,用于等分容器 **/
.demo {
flex: 1;
}

flex 属性是 flex-grow,flex-shrink,flex-basis 三个属性的缩写。而flex: 1的完整写法就是1 1 0%,这点浏览器上就可以看到:
这三个属性,解释起来也很简单。flex-grow 表示弹性盒容器存在剩余宽度时按照该比例分配剩余空间。flex-shrink 则表示弹性盒容器宽度不足时,如何按照比例缩小子项的宽度。那么如何判断弹性盒容器是有剩余空间,还是空间不足呢,就是根据 flex-basis 属性,可以理解为基础宽度,子项的基础宽度相加大于弹性盒宽度,那就是空间不足,否则就是存在剩余空间,flex-grow 和 flex-shrink 就是来分配这个剩余空间和缩小空间以适应空间。 举例来看:

css

.demo5 {
display: flex;
}
.demo5-child:nth-child(1) {
flex: 0 1 100px;
}
.demo5-child:nth-child(2) {
flex: 0 1 100px;
}
.demo5-child:nth-child(3) {
flex: 0 1 100px;
}
.demo5-child:nth-child(4) {
flex: 0 1 100px;
}

上面的设置0 1 100px就表示 flex-grow 为0,有剩余空间不进行分配,flex-shrink 为1,表示空间不足时,所有空间按照比例进行缩放,flex-basis 为 100px,表示基础尺寸为 100px。 如果既要分配剩余空间,又要根据容器进行缩放,可以把上面的子项 flex 修改一下:

css

.demo5-child {
flex: 1 1 100px;
}

面试中经常会问到,flex-grow、flex-shrink 的缩放规则是什么?这里以上面的例子简单说明一下。 假设初始容器宽度为 1000,flex 属性为1 1 100px,那么总共的基础宽度就是 400px,此时,剩余宽度为 1000 - 400 = 600px,flex-grow 就是用来分配这个 600px 的宽度,总的比例加起来为 4,每个子项比例就是 1 / 4,那么每项增加的宽度 600 * 1 / 4 = 150px,那么加上基础宽度 100px,就是 250px,也就是说当容器宽度变成 1000px,每个子项的宽度就变成了 250px。
缩放也是同理,假设现在容器宽度缩小到 300px,那么不足的空间是 100px,flex-shrink 的缩小比例也是 1,每项总的缩小也是 1 / 4,那么每项缩小的宽度就是 100px * 1 / 4 = 25px,那么子项的宽度就变成了 100px = 75px 了。
利用 flex 属性,就能很轻松的实现自适应布局,比如经常使用的一栏定宽,一栏自适应,使用 flex: 1就能轻松实现,而不必借助于 max-width、min-width 来设置最大最小宽度。

未完待续

CSS 细节太多,很多看似所有人都这么写的代码,反而可能是不合适的,每种布局都有其合适的 CSS,想要写出一个稳定、高效的布局,了解 CSS 背后的细节不能少。很多人觉得 CSS 不重要,写出来不就行了,管它什么合适不合适,一旦遇到问题,可能就会需要花费更多时间来解决问题,甚至出现一些奇怪的现象,不了解的话犹如无头苍蝇,只能一个一个属性去试能否解决。 看完此篇,希望能对你的 CSS 有更深的了解认识。

链接:https://juejin.cn/post/7244106756677271611

返回列表页>