CSS世界笔记

参考资料

CSS世界


盒尺寸四大家族

深入理解content

content与替换元素

根据外在盒子是内联还是块级,我们把元素分为内联与块级元素,而根据是否具有可替换内容,我们可将元素分为替换元素和非替换元素。

替换元素,顾名思义,内容可以被替换,如:

<img src="xx1.png">

我们可以把上面的xx1.png替换为xx2.png,图片就替换了?这种通过修改某个属性值呈现的内容就可以被替换的元素称为替换元素,因此img、object、video、iframe、textarea、input等都是替换元素

替换元素除了内容可替换外,还有以下特性:

  • 内容的外观不受页面上的css的影响。那样式又该如何改变呢?需要类似appearance属性或者浏览器自身暴露的一些样式接口,如:::ms-check{}可以更改高版本IE浏览器下单复选框的内间距,背景色等,但直接input[type='checkout']{}却无法更改内间距,背景色等。
  • 有自己的尺寸。在web中,很多替换元素在没有明确尺寸设定的情况下,其默认的尺寸(不包括边框)是300像素x150像素,如<video>、<iframe>、<canvas><img>却是0,而表单元素的替换元素的尺寸和浏览器实现有关。
  • 在很多css属性上有自己的一套表现规则。比如vertical-align属性,对于替换元素和非替换元素意义是不一样的,对于普通的非替换元素,默认值为baseline,而对于替换元素(比如img等)则定义为了元素下边缘。

注意:替换元素都是内联级水平元素。因此是可以一行显示的。

替换元素的尺寸计算规则

可以分为:固有尺寸,HTML尺寸,CSS尺寸三类

  • 固有尺寸是替换内容原本的尺寸。对于图片、视频等作为一个独立文件存在时,都有自己的高度和宽度,这些便可以理解为固有尺寸。
  • HTML尺寸,可以想象成鸡蛋里的那层很薄的膜,里面的蛋清和蛋黄就是固有尺寸,而CSS尺寸就是蛋壳。而HTML尺寸只能通过HTML属性进行改变,比如:
<img width="300" height="100">
<input type="file" size="30">
<textarea cols="20" rows="5"></textarea>
  • CSS尺寸特指那些通过CSS的widthheight或者max-width/min-widthmax-height/min-height设置的尺寸

当多种尺寸共存时,最终尺寸的结果如何确定呢?

  • 当没有CSS尺寸和HTML尺寸时,则使用固有尺寸,如<img src="xxx.png"/>
  • 如果没有CSS尺寸,但有HTML尺寸,如<img src="xxx.png" width="128" height="96"/>,这里使用了HTML尺寸,则最终尺寸由HTML尺寸决定
  • 如果有CSS属性,则最终有CSS属性决定
<style>
  img { width: 200px; height: 150px; }
</style>
<img src="1.jpg" width="128" height="96">
  • 如果固有尺寸含有固有的宽高比,同时仅设置了宽度或高度,则按比例缩放另一个尺寸,比如下面仅设置了宽度,则可以自动按比例算出高度。
<style>
  img { width: 200px; }
</style>
<img src="1.jpg">
  • 如果上面的所有情况都不满足,则最终宽度表现为300像素,高度为150像素,如<video><video>
  • 如果是内联元素和块级替换元素,则同样使用上面的规则,如图片是内联,但变成了块级,此时尺寸规则还是和内联状态下一致。这也是为何图片或其他表单类替换元素设置成display:block后,宽度却没有100%容器的原因。

img元素还有其他属性,比如:

  • srcset 属性其实就是根据浏览器宽、高和像素密度来加载相应的图片资源,属性格式:图片地址 宽度描述w 像素密度描述x,多个资源之间用逗号分隔。如
<!-- 下面的例子表示浏览器宽度达到 800px 则加载 middle.jpg ,达到 1400px 则加载 big.jpg。注意:像素密度描述只对固定宽度图片有效。 -->
<img src="small.jpg " srcset="big.jpg 1440w, middle.jpg 800w, small.jpg 1x" />
  • size 属性给浏览器提供一个预估的图片显示宽度。
<!-- 下面的例子表示浏览器视口为 320px 时图片宽度为 300px,其他情况为 1200px。。 -->
<img src="images/gun.png" alt="img元素srcset属性浅析"
         sizes="(max-width: 320px) 300w, 1200w"/>
  • image-set属性,支持根据用户分辨率适配图像
/* 下面的例子普通屏幕使用 pic-1.jpg,为高分屏使用 pic-2.jpg,如果更高的分辨率则使用 pic-3.jpg。 */
body {
    background-image: -webkit-image-set( url(../images/pic-1.jpg) 1x, url(../images/pic-2.jpg) 2x, url(../images/pic-3.jpg) 600dpi);
    background-image: image-set( url(../images/pic-1.jpg) 1x, url(../images/pic-2.jpg) 2x, url(../images/pic-3.jpg) 600dpi);
}

内联元素与流

字母x

字母x与CSS基线

基线是如何定义的?字母x的下边缘就是我们常说的基线。另外CSS中有一个概念x-height指的就是小写字母x的高度,也就是基线与等分线之间的距离,如下图:

x-height

  • ascender height,上下线高度
  • cap height,大写字母高度
  • median,中线
  • descender height,下行线高度

我们常用的vertical-align:middlemiddle并不是上图的中线,而是1/2 x-height高度,也就是字母x交叉点。

因此vertical-align:middle并不是绝对的垂直居中对齐,middle效果只是一种近似效果,因为不同的字体在行内盒子的位置是不一样的。

字母x与CSS中的ex

字母x衍生出一个x-height概念,并进一步衍生出ex,而ex是css中地地道道的一个尺寸单位。指的就是小写字母x的高度,就是x-height的高度。

注意,虽说em、px这类单位的主要作用是限定元素的尺寸,但是,由于字母x受字体等CSS属性影响大,因此ex不太适合用来限定元素的尺寸。。。那ex什么时候用呢?

最佳场景:不受字体和字号影响的内联元素的垂直居中对齐效果

内联元素默认是基线对齐,而基线就是x的底部,1ex就是一个x的高度。如果此时一个图标的高度就是1ex,同时图标的背景图片居中,岂不是图标和文字天然垂直居中,而且完全不受字体和字号的影响。。。这时就完全没必要用vertical-align:middle了,如下:

<style>
.icon-arrow {
  display: inline-block;
  width: 20px;
  height: 1ex;
  background: url(https://demo.cssworld.cn/images/5/arrow.svg) no-repeat center;
  background-size: 100% 100%;
}
</style>

<i class="icon-arrow"></i>和文字中间对齐

内联元素的基石line-height

内联元素的高度之本line-height

默认空的<div>高度为0,但是里面写几个文字,则高度就有了,这个高度是怎么来的或者是由哪个CSS属性决定的?

我们直观的感觉<div>高度是因为font-size决定的,但本质上是由line-height属性全权决定的。

<div class="test1">我的高度是?</div>
<style>
.test1 {
  font-size: 16px;
  /* 行高为0 */
  line-height: 0;
  border: 1px solid #ccc;
  background: #eee;
}
</style>

<div class="test2">我的高度是?</div>
<style>
.test2 {
  /* 字体大小为0 */
  font-size: 0;
  line-height: 16px;
  border: 1px solid #ccc;
  background: #eee;
}
</style>

打开浏览器就可以看到,test1高度变成了一条线,而test2虽然文字没有了,但是高度依然存在。。。因此,div的高度是行高决定的,而非文字大小

流的破坏与保护

CSS中,标准数据流总是挨个平铺整个页面,但有时为了实现一些效果,就必须要破坏这种标准数据流结构,当然有破坏就需要保护,也就是破坏流和保护流的CSS属性。

魔鬼属性float

1. float的本质与特质
早期的float属性是用来实现文字环绕效果的,而这种文字环绕,主要指的就是文字环绕图片显示的效果。也就是说CSS2属性的设计都是为图文展示服务的,float也是如此。

而现在很多用float进行布局,会出现很多意外的情况,其实是对其的滥用。但既然用了,就得解决其带来的各种问题,首先来看看float属性的特性:

  • 包裹性
  • 块状化并格式化上下文
  • 破坏文档流
  • 没有任何margin合并

1. 包裹性其实包含两部分:包裹和自适应性

包裹:假设浮动元素父元素宽200px,浮动子元素为128px宽的图片,此时浮动元素宽度表现为包裹性,也就是图片的宽度128px

<style>
  .father { width: 200px; }
  .float { float: left; }
  .float img { width: 128px; }
</style>

<div class="father">
  <div class="float">
    <img src="test.jpg">
  </div>
</div>

自适应:假设浮动元素子元素不单单有128px宽的图片,还有一段文字。此时浮动元素宽度自适应宽度就是父元素的200px,当然要想自适应父元素的宽度,一定是浮动元素内部元素的首选最小宽度(其实就是是否可换行显示)比父元素的宽度小的前提下。如果文字是一串很长不间断的英文,则英文默认无法断开,因此总体下来就会超过200px

<style>
  .father { width: 200px; }
  .float { float: left; }
  .float img { width: 128px; }
</style>

<div class="father">
  <div class="float">
    <img src="test.jpg">此时宽度依据文字首选最小宽度与图片的总和

    <!-- 换成英文,无法折行,超过200px -->
    <img src="test.jpg">ddoeohfoiwewepquwpeurqpweurpwquterpweutiwt
  </div>
</div>

2. 块状化float属性一旦不为none,则display就为block或table,可以用如下代码测试:

var span = document.createElement('span');
document.body.appendChild(span);
console.log('1. ' + window.getComputedStyle(span).display); // 1. inline

// 设置浮动
span.style.cssFloat = 'left';
console.log('2. ' + window.getComputedStyle(span).display); // 2. block

只有一个属性display:inline-block;设置为float后,display:table。既然属性变为了block后,则有些设置就没有了意义,如下:

span {
  display: block;         /* 多余 */
  float: left:
}
span {
  vertical-align: middle; /* 多余,块级没有基线问题 */
  float: left:
}
span {
  text-align: right;      /* 谷歌浏览器测试有效 */
  float: left:
}

注意:虽然设置了float后,display属性表现为block或table,但并不是说此时样式就自动撑满屏幕了(块级特性)。

2. float的作用机制
float有个突出表现,就是会让父元素的高度塌陷,其实这种塌陷才是正常。。。因为float就是为了实现文字环绕效果。比如如何让如下代码实现文字围绕图片?

<div class="father">
  <img src="test.jpg">
</div>
<p class="animal">这是文字,这是文字,...</p>

因为div、p都是块级会自动撑满整个屏幕。要想实现文字环绕,只需给img标签添加float:left即可,此时div高度塌陷为0。。。这里也可以看出,float属性让父元素高度塌陷的原因就是为了实现文字环绕效果,但随着发展,float常被用来布局。。。而布局的话,高度塌陷就成为了问题

高度塌陷,跟随的内容也就可以和浮动元素在一个水平线上了,但这只是实现“环绕效果”的条件之一,要想实现真正的“环绕效果”,就需要另外一个平时大家不太在意的特性,那就是行框盒子和浮动元素的不可重叠性。。。

这里解释下行框盒子,其实对于<p>这是文字</p>来说,p块级盒子,而“这是文字”是具体的内容,而就在这个具体的内容上有个**行框盒子**,如果非要用代码显示出来的话就是:

<style>
.animal:first-line {
  backgroud:pink;
  color: blue;
}
</style>

<div class="father">
  <img src="test.jpg" style="float:left;">
</div>
<p class="animal">这是文字,这是文字,...</p>

也就是每行内联元素都会默认在一个隐藏的盒子里,而这个盒子就是行框盒子,注意:是每行内联元素。而且这个隐藏的盒子无法用CSS属性改变。

其实由于img浮动导致div高度塌陷,因此也就导致了pimg的重叠。。。只是由于行框盒子与浮动元素无法重叠,因此文字才会被挤到图片右侧,进而产生文字环绕图片的效果。

问:如果一个元素设置具体的高度,就不需要担心float导致的高度塌陷了?

答:对了一半,因为文字环绕效果是由两个特性(父级元素高度塌陷和行框盒子区域限制)共同作用,因此定高只解决了前者。因此当浮动元素在垂直区域一旦超过高度范围,后者下面元素的margin-top为负值,就很容易使后面的元素发生环绕效果,如下:。

<style>
.father {
  height: 64px;
  border: 1px solid #444;
}
.float {
  float:left;
}
.float img {
  width: 60px; height: 64px;
}
</style>

<div class="father">
  <div class="float">
    <img src="zxx.jpg">
  </div>
  我是帅哥,好巧啊,我也是帅哥,原来看这本书的人都是帅哥~
</div>
<div>虽然你很帅,但是我对你不感兴趣。</div>

虽然father设置高度为64px,但是内联元素的img底部是有间隙的,也就是说.float这个浮动元素的实际高度是大于64px的。因此就会导致最后一个div里的文字也发生了环绕现象(因为行框盒子与浮动元素不可重叠)

3. float与流体布局
虽然float可以导致塌陷问题,但稍加改造就可以实现多种布局模式:

<!-- 一侧定宽的两栏自适应 -->
<style>
CSS
.father {
  border: 1px solid #444;
  overflow: hidden;
}
.father > img {
  width: 60px;            /* 宽度还可以使用百分比*/
  height: 64px;
  float: left;
}
.girl {
  /* 环绕和自适应的区别所在 */
  margin-left: 70px;      /*还可以使用百分比*/
}
</style>

<div class="father">
  <img src="zxx.jpg">
  <p class="girl">美女1,美女2,美女3,美女4,美女5,美女6,后宫1,后宫2,后宫3,后宫4,后宫5,后宫6</p>
</div>
<!-- 三栏自适应布局 -->
<style>
CSS
.prev {
  float: left;
}
.next {
  float: right;
}
.title {
  margin: 0 50px;
}
</style>

<div class="box">
  <a class="prev">上一页</a>
  <a class="next">下一页</a>
  <h3 class="title">两侧环绕</h3>
</div>

4. float的天然克星clear
CSSS中有属性专门用来处理float属性带来的高度塌陷问题。语法如下:

  • clear: none | left | right | both

我们常说这样便清除了浮动,其实并没有。。。,官方的clear的解释是:元素盒子的边不能和前面的浮动元素相邻。其实就是说使用了这个属性后,就不能挨着浮动元素布局了

我们常用clear:both,而很少使用left、right,这是因为但凡后者有效,都可以用前者来替代。

假设容器宽度足够,有10个li元素,设置如下样式:

li {
  width: 20px; height: 20px;
  margin: 5px;
  float: left;
}
/* 匹配相同兄弟元素,这里是li */
li:nth-of-type(3) {
  clear: both;
}

最终结果,将会显示两行,第一行有两个li,而第二行8个li。。。这是因为clear属性让自身不能和前面的浮动元素相邻,而对后面浮动元素不管不问。。。但注意,如果设置clear: right,则只显示一行,因为li浮动是左浮动,设置右侧清除浮动没有意义。。。除非设置为右浮动,则同样会显示两行。

因此:为了避免纠结,就直接用clear: both

5. clear的不足
clear属性只有块级元素才有效,而::after等伪元素都是内联水平,因此借助伪元素消除浮动,需要设置display的原因:

.clear::after {
  content : '';
  display : block; // 或 table、list-item
  clear : both;
}

由于clear : both;的本质是让自己不和float元素在一行显示,并不是真正意义上的清除浮动,因此依然会有不好的特性存在。

<style>
.father {
  border: 1px solid #444;
  overflow: hidden;
}
.father > img {
  width: 60px;
  height: 64px;
  float: left;
}
.animal {
  margin-left: 70px;
}
.clear {
  clear: both;
}
</style>

<div class="father">
  <img src="https://demo.cssworld.cn/images/common/l/zxx.jpg" />
  <div class="animal">
    小猫1,小猫2,
    <div class="clear"></div>
    小猫3,小猫4,小猫5,小猫6,小狗1,小狗2,小狗3,小狗4,小狗5,小狗6
  </div>
</div>
  • 如果clear : both;元素前面的元素就是float元素,则margin-top负值即使-9999px,也没有效果
  • clear : both;后面的元素依旧可能会发生文字环绕的现象。

因此clear:both只能在一定程度上消除浮动的影响,要想完美的去除浮动元素的影响,还需要其他的CSS声明。

BFC

BFC的定义

BFC全称为block formatting context,中文为:块级格式化上下文。相对应的还有IFC, inline formatting context,中文为:内敛格式化上下文。

可以通俗的理解:BFC为某个元素的一个css属性,只不过这个属性不能被开发者显式的修改,拥有这个属性的元素对内部元素和外部元素会表现出一些特性,这就是BFC

触发生成BFC的条件:

  1. 根元素,即HTML元素
  2. float的值不为none
  3. overflow的值不为none
  4. display的值为inline-block、table-cell、table-caption
  5. position的值为absolute或fixed

也就是说,只要元素符合上面任意一个条件,就无需clear:both属性去清除浮动的影响了。也就不需要见到浮动就添加.clearfix的类名了。

BFC与流体布局

BFC最重要的用途其实不是消除margin重叠或消除float的影响,而是实现更健壮,更智能的只适应布局,如下解决文字环绕:

<style>
  img { float: left; }
</style>
<div class="father">
  <img src="https://demo.cssworld.cn/images/common/l/zxx.jpg">
  <p class="animal">小猫3,小猫4,小猫5,小猫6,小狗1,小狗2</p>
</div>

当我们给animal设置了BFC特性的属性,如overflow:hidden,根据BFC的表现原则,具有BFC特性的元素的子元素不会受到外部元素影响,也不会影响外部元素。

也就是,普通流体元素在设置了overflow:hidden后,会自动填满容器中除了浮动元素以外的剩余空间,形成自适应布局效果,而这种效果要比纯流体自适应更加智能。

BFC表现出的特性: 1、同一个BFC下,边距合并

 <!-- 1,在同一个BFC下,外边距会发生重叠
    如下,第一个div的margin-bottom会与第二个div的margin-top合并
    取二者大值,都是50px,所以合并后为50px
     -->
<body>
  <div style="width: 50px; height: 50px; margin: 50px;"></div>
  <div style="width: 50px; height: 50px; margin: 50px;"></div>
</body>
<!-- 1.1,如果不想合并,只需将其中给一个或都改为BFC即可,如下 -->
<!-- 如果直接给第二个div添加overflow无效。可添加display: inline-block; -->
<body>
  <div style="width: 50px; height: 50px; margin: 50px;"></div>
  <div style="overflow: hidden;">
    <div style="width: 50px; height: 50px; margin: 50px;"></div>
  </div>
</body>

2、同一个BFC下,浮动元素不占空间

<!-- 内部的div虽然有高度,但无法撑开外层div,也就是父元素塌陷 -->
<div style="border: 1px solid #ccc">
  <div style="width: 50px;height: 50px;float: left;"></div>
</div>

<!-- 利用BFC思想,给父元素添加触发生成BFC条件的任意一条,都可以撑开 -->
<div style="border: 1px solid #ccc; overflow: hidden">
  <div style="width: 50px;height: 50px;float: left;"></div>
</div>

3、文字环绕和三栏自适应布局

<!-- float的设计之初就是文字环绕用 -->
<div>
  <div style="width: 50px;height: 50px;float: left;"></div>
  <div>这是一段文字,应该围绕左侧盒子布局;</div>
</div>

<!-- 利用BFC思想,添加overflow: hidden,可以不遮挡也不环绕 -->
<!-- 注意对文字来说有点特殊,要针对性应用属性 -->
<div>
  <div style="width: 50px;height: 50px;float: left;"></div>
  <div style="overflow: hidden;">这虽然是一段文字,但现在应该不再围绕左侧盒子布局;</div>
</div>

<!-- 利用BFC思想,实现三栏布局 -->
<div>
  <div style="width: 50px;height: 50px;float: left;"></div>
  <div style="width: 50px;height: 50px;float: right;"></div>
  <div style="overflow: hidden;">这是一段文字,应该始终位于中间位置</div>
</div>

其实对于BFC处理最佳方案是overflow:hidden,因为该声明既不会影响元素原先的流体特性或宽度表现。不过overflow属性原本的作用指定了容器元素的内容溢出时是否需要裁剪,而BFC特性只是其衍生出来的特性,裁剪才是其本质工作。

最近的文章

webpack使用

webpack中文文档 老版本 v4.15.1版本 v4.26.0版本(最新) v4新版本变化 webpack v1迁移到v2webpack解说版本一1、webpack管理pageage的好处很早之前,我们引用第三方依赖的方式,是通过script标签引入,这会有以下几个问题: 需要确保依赖下载完成之后才能使用 需要确保依赖的引入顺序 引入的依赖如果没有被使用,浏览器也会下载,占带宽 第三方依赖发生变化后,需要重新引入新的url地址因此,我们用webpack来管理这些脚本,...…

继续阅读
更早的文章

Es6的那些高级特性

Module的语法历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES...…

继续阅读