第4章 盒尺寸四大家族

深入理解content

content与替换元素

1. 什么是替换元素

通过修改某个属性值呈现的内容就可以被替换的元素称为替换元素。因此,imgobjectvideoiframe或者表单元素textareainput都是典型的替换元素。

替换元素除了内容可替换这一特性外,还有一些特性:

  1. 内容的外观不受页面上的CSS的影响。样式表现在CSS作用域之外。更改替换元素自身的外观需要浏览器自身暴露的一些样式接口。
  2. 有自己的尺寸。替换元素在没有明确尺寸设定的情况下,其默认的尺寸(不包括边框)是300px*150px
  3. 在很多CSS属性上有自己的一套表现规则。比较具有代表性的就是vertical-align属性。对于非替换元素,vertical-align的默认值是baseline,被定义为字符x的下边缘;对于替换元素的内容没有字符x,替换元素的基线就被定义为元素的下边缘。

2. 替换元素的默认display

所有的替换元素都是内联元素,也就是替换元素和替换元素、替换元素和文字都是可以在一行显示的。

各个替换元素的默认display属性值(P46)

inputbutton按钮的区别在什么地方?区别在于两种按钮默认的white-space值不一样,前者是pre,后者是normal,所表现出来的差异是:当按钮文字足够多时,input按钮不会自动换行,button按钮则会。

替换元素的displayinlineblockinline-block中的任意一个,其尺寸计算规则都是一样的。

3. 替换元素的尺寸计算规则

替换元素的尺寸从内到外分为3类:固有尺寸、HTML尺寸和CSS尺寸。

  1. 固有尺寸指的是替换内容原本的尺寸。
  2. HTML尺寸imginput只能通过HTML原生属性改变。
  3. CSS尺寸指的是可以通过CSS的widthheight或者max-width/min-widthmax-height/min-width设置的尺寸,对应盒尺寸的content box

可以影响替换元素的3层结构(由里到外):固有尺寸->HTML尺寸->CSS尺寸

这3层结构的计算规则:

<img src="1.jpg" alt="">

页面显示宽高就是图片自身的尺寸256px*192px

<img src="1.jpg" width="128" height="128" alt="">

通过HTML属性widthheight限定了图片的HTML尺寸,因此最终图片所呈现的宽高就是128px*128px

img {
    width: 200px;
    height: 150px;
}
<img src="1.jpg" width="128" height="128" alt="">

此时,固有尺寸、CSS尺寸和HTML尺寸同时存在,起作用的是CSS属性限定的尺寸,因此,最终图片所呈现的宽高就是200px*150px

img {
    width: 200px;
}
<img src="1.jpg" alt="">

设置的宽度,因为图片自身有着固定的宽高比例,所以最终图片呈现的宽高就是200px*150px150=200+192/256

实际开发中,为了提高加载性能和节约带宽费用,首屏图片采用滚屏的方式异步加载,并且使用一张透明的图片占位。一般直接使用<img>

img {
    visibility: hidden;
}

img[src] {
    visibility: visible;
}

img标签中有src属性时,即使为空,浏览器依然会发起请求,而且请求的是当前页面数据。当图片的src缺省时,图片不会有任何请求,是最高效的实现方式。

Firefox下img的表现是一个内联元素,而非替换元素,因此很多设置都它下面无效。要修复这个问题非常简单,就是直接设置img {display:inline-block;}

在CSS中,图片的固有尺寸是无法改变的,显示的仅仅是设定的content box尺寸,图片中的content替换内容默认的适配方式是填充fill
尺寸变化的本质并不是改变固有尺寸,而是采用了填充作为适配HTML尺寸和CSS尺寸的方式。
在CSS3中,img以及其它替换元素的适配方式可以通过object-fit属性进行修改。

4. 替换元素和非替换元素的距离

观点1:替换元素和非替换元素之间只隔了一个src属性

如果把imgsrc属性去掉,img就是一个和span类似的普通的内联标签,也就变成了一个非替换元素。

img {
    display: block;
    outline: 1px solid;
}
<img>

在Firefox下,最终的宽度是100%自适应父容器的可用宽度。span标签设置widthheight是无效的。如果设置<img alt="图片">不为空的alt值,Chrome下也会有同样的表现。

在IE中有个默认的占位替换内容,当src属性缺失时,会使用这个默认的占位内容,这也是IE浏览器下默认img尺寸是28*30,而不是Chrome下的0*0的原因。

另一个证明替换元素和非替换元素的区别在于src属性的实例就是基于伪元素的图片内容生成技术。

可以对img元素使用::before::after伪元素进行内容生成以及样式构建,为了解决兼容性问题,需要注意一些技术点:

  1. 不能有src属性(关键所在)
  2. 不能使用content属性生成图片(针对Chrome)
  3. 需要有alt属性并且有值(针对Chrome)
  4. Firefox下::before伪元素的content值会被无视,::after无此问题,应该与Firefox自己占用了::before伪元素的content有关。

基于伪元素的图片内容生成技术,在图片还没有加载时把alt信息呈现出来。

实例Demo:src缺省时img元素的alt信息展示

See the Pen 替换元素src by whjin (@whjin) on CodePen.

观点2:替换元素和非替换元素之间只隔了一个CSScontent属性

content属性决定了是替换元素还是非替换元素。

以下两个实例是等效的:

//组一
img {
    content: url("https://www.baidu.com/img/bd_logo1.png");
}
<img>

<img src="https://www.baidu.com/img/bd_logo1.png">

//组二
<img src="https://www.baidu.com/img/bd_logo1.png">

img:hover {
    content: url("logo.jpg");
}   

使用content属性,可以让普通标签元素变成替换元素。

5. content与替换元素关系

content生成的内容和普通内容有很多不同的特性表现:

  1. 使用content生成的文本无法选中、无法复制,无法被屏幕阅读设备读取,也无法被搜索引擎抓取。
  2. 不能左右:empty伪类。:empty是一个CSS选择器,当元素里面无内容时进行匹配。
  3. content动态生成值无法获取。content可以实现计算器效果,可以自动累加数值。

content内容生成技术

1. content辅助元素生成

.element:before {
    content: '';
}

辅助元素最常见的应用就是清除浮动带来的影响:

.clear:after {
    content: '';
    display: table; /*也可以是block*/
    clear: both;
}

另一个很具有代表性的应用就是辅助实现“两端对齐”以及“垂直居中/上边缘/下边缘对齐”效果

:before伪元素用于辅助实现底对齐,:after伪元素用于辅助实现两端对齐。

2. content字符内容生成

直接写入字符内容,常见应用是配合@font-face规则实现图标字体效果。

插入Unicode字符,典型应用是插入换行符来实现某些布局或效果。

:after {
    content: '\A';
    white-space: pre;
}

\A是换行符中的LF字符,其Unicode编码是000A,在CSS的content属性中则直接写成\A;换行符中的CR字符,其Unicode编码是000D,在CSS的content属性中则直接写成\D。分别指回车CR和换行LF

See the Pen content字符内容生成 by whjin (@whjin) on CodePen.

3. content图片生成

base64图片由于内联在CSS文件中,直接出现没有尺寸为0的状态,同时无须额外设置display:block/inline-block;,CSS代码更省。

4. content开启闭合符号生成P63

5. content attr属性值内容生成

.icon:before {
    content: attr(data-title);
}

6. 深入理解content计数器

  1. 属性counter-reset,计数器重置,还可以设置为noneinherit。取消重置以及继承重置。
  2. 属性counter-increment,计数器递增,计数器数值变化遵循HTML渲染顺序,遇到一个increment计数器就变化,counter输出的时候就是此时的计数值。

    counter-increment的其他特性:

  3. 方法counter()/counters()

coutners()方法就是嵌套计数,基本用法是counters(name,string);,其中,string参数为字符串(需要引号,是必需参数),表示子序号的连接字符串。

实现嵌套,必须让每个列表容器拥有一个唯一的计数源,通过子辈对父辈的counter-reset重置、配合counters()方法才能实现计数嵌套效果。

一个容器的counter-reset是唯一的,一旦子元素出现counter-reset。就会改变整个容器的嵌套关系。

counters()支持style自定义递增形式:

counters(name, string, style);

7. content内容生成的混合特性

content内容生成的混合特性指的是各种content内容生成语法可以混合在一起使用。

温和的padding属性

padding与元素的尺寸

CSS中默认的box-sizingcontent-box,使用padding会增加元素的尺寸。

内联元素的padding在垂直方向会影响布局,影响视觉表现。内联元素没有可视宽度/高度(clientWidth/clientHeight永远为0),垂直方向的行为表现完全受line-heightvertical-align的影响。

利用内联元素的padding实现高度可控的分割线。

网页通过地址栏的hash值和页面HTML的id值一样发生锚点定位。

内联元素设置padding不会影响布局,但是块级元素就会。

<h3><span id="hash">标题</span></h3>
h3 {
    line-height: 30px;
    font-size: 14px;
}

h3 > span {
    padding-top: 58px;
}

对于非替换元素的内联元素,不仅padding不会加入行盒高度的计算,margin/border也都是如此,都是不计算高度,但实际上在内联盒周围发生了渲染。

padding的百分比值

padding属性值不支持负值,支持百分比值,padding百分比值无论是水平方向还是垂直方向均是相对于宽度计算。

实现一个固定比例(宽高比)的头图效果。

.box {
    padding: 10% 50%;
    position: relative;
}

.box > img {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
}

内联元素和块状元素应用百分比值的表现:

内联元素的垂直padding会让“幽灵空白节点”出现。

内联元素默认的高度完全受font-size大小控制。通过以下方法使得“幽灵空白节点”高度变为0

span{
    padding: 50%;
    font-size: 0;
    background-color: gray;
}

标签元素内置的padding

  1. ol/ul列表内置padding-left,单位是px,如果列表中的font-size值很小,则li元素的项目符号就会在ol/ul元素的左边缘距离很开。
  2. 表单元素大都内置padding

使用label元素解决button按钮在不同浏览器下padding表现不一致的问题

button {
    position: absolute;
    clip: rect(0 0 0 0);
}

label {
    display: inline-block;
    line-height: 20px;
    padding: 10px;
}

padding与图形绘制

“三道杠”小图标

<div class="icon-menu"></div>
.icon-menu {
    display: inline-block;
    width: 100px;
    height: 8px;
    padding: 20px 0;
    border-top: 8px solid;
    border-bottom: 8px solid;
    background-color: currentColor;
    background-clip: content-box;
}

“双层圆点”图

<div class="icon-dot"></div>
.icon-dot {
    display: inline-block;
    width: 100px;
    height: 100px;
    padding: 10px;
    border: 10px solid;
    border-radius: 50%;
    background-color: currentColor;
    background-clip: content-box;
}

激进的margin属性

margin与元素尺寸以及相关布局

1. 元素尺寸

2. margin与元素的内部尺寸

一侧定宽的两栏自适应布局效果

  1. 如果图片左侧定位:
<div class="box">
    <img src="1.jpg" alt="">
    <p>文字内容...</p>
</div>
.box {
    overflow: hidden;
}

.box > img {
    float: left;
}

.box > p {
    margin-left: 140px;
}

文字内容根据.box盒子的宽度变化而自动排列,形成自适应布局效果。

  1. 如果图片右侧定位:改变浮动和margin方向。借助margin负值定位实现。
  2. 如果图片右侧定位,同时顺序一致:
<div class="box">
    <div class="full">
        <p>文字内容...</p>
    </div>
    <img src="1.jpg" alt="">
</div>
.box {
    overflow: hidden;
}

.full {
    width: 100%;
    float: left;
}

.box > img {
    float: left;
    margin-left: -128px;
}

.full > p {
    margin-right: 140px;
}
  1. 如果图片右侧定位,同时顺序一致。

需求:列表块两端对齐,一行显示3个,中间有2个20px的间隙。

ul {
    list-style-type: none;
    margin-right: -20px;
}

ul > li {
    float: left;
    width: 100px;
    margin-right: 20px;
    background-color: #a0b3d6;
}

3. margin与元素的外部尺寸

借助margin的外部尺寸特性来实现底部留白。只能使用子元素的margin-bottom来实现滚动容器的底部留白。

<div style="height: 200px;">
    <img src="1.jpg" alt="" style="margin: 50px 0;">
</div>

利用margin外部尺寸实现等高布局。此布局多出现在分栏有背景色或中间有分割线的布局中。height:100%需要在父级设定具体高度值时才有效。

<div class="column-box">
    <div class="column-left"></div>
    <div class="column-right"></div>
</div>
.column-box {
    overflow: hidden;
}

.column-left, .column-right {
    margin-bottom: -9999px;
    padding-bottom: 9999px;
}

等高布局实现原理

垂直方向margin无法改变元素的内部尺寸,但却能改变外部尺寸。默认情况下,垂直方向块级元素上下距离是0,一旦设置margin-bottom:-9999px;后面所有元素和上面元素的空间距离变成-9999px,就是后面元素都往上移动9999pxpadding-bottom:9999px;增加元素高度,正负一抵消,对布局层并无影响,但视觉层多了9999px高度的可使用的背景色。配合overflow:hidden;把多余的色块背景隐藏,实现视觉上的等高布局效果。

margin的百分比值

元素设置margin在垂直方向上无法改变元素自身的内部尺寸,往往需要父元素作为载体,此外,由于margin合并的存在,垂直方向往往需要双倍尺寸才能和padding表现一致。

margin合并

块级元素的上边距margin-top与下边距margin-bottom有时会合并为单个外边距。

  1. 块级元素,但不包括浮动和绝对定位元素。
  2. 只发生在垂直方向,默认文档流是水平的,因此发生margin合并就是垂直方向。

margin合并的3种场景

  1. 相邻兄弟元素margin合并。
  2. 父级和第一个/最后一个子元素。

阻止margin合并

对于margin-top合并,可以进行如下操作(满足一个条件即可):

对于margin-bottom合并,可以进行如下操作(满足一个条件即可):

margin合并导致头图掉下来可以添加.container{overlfow:hidden;}进行修复。

其原理是通过设置overflow属性让父级元素块状格式化上下文。

  1. 空块级元素的margin合并。border阻断margin合并。

不希望空div元素有margin合并,可以进行如下操作:

3. margin合并的计算规则

margin合并的计算规则总结为正正取大值正负值相加负负最负值

4. margin合并的意义

合并机制可以保证元素上下间距一致。

父子margin合并的意义在于:在页面上任何地方嵌套或直接放入任何空div,都不会影响原来的块布局。

遇到列表或模块,全部保留上下margin设置:

.list {
    margin-top: 15px;
    margin-bottom: 15px;
}

margin:auto

  1. 元素没有设置width/height,也会自动填满容器。
  2. 元素没有设置width/height,也会自动填满包含块容器。

margin:auto的填充规则:

  1. 如果一侧定值,一侧auto,则auto为剩余空间大小。
  2. 如果两侧均是auto,则平分剩余空间。

auto用于计算对应方向所获得的剩余空间大小。

See the Pen margin:auto-1 by whjin (@whjin) on CodePen.

实现右对齐效果,margin属性的auto计算就是为块级元素左中右对齐而设计的,和内联元素使用text-align控制左中右对齐相对应。

See the Pen margin-auto左中右居中 by whjin (@whjin) on CodePen.

居中对齐左右同时auto计算即可

See the Pen margin-auto-3 by whjin (@whjin) on CodePen.

绝对定位垂直水平居中

See the Pen margin-auto垂直水平居中 by whjin (@whjin) on CodePen.

margin无效情形解析

  1. display计算值inline的非替换元素的垂直margin无效。
  2. 表格中trtd元素或设置display计算值是table-celltable-row的元素的margin都是无效。
  3. margin合并的时候,更改margin值可能没有效果。
  4. 绝对定位元素非定位方位的margin值无效。绝对定位元素的渲染是独立的,所以设置了topleft方位,再设置margin-right无效。
  5. 定高容器的子元素的margin-bottom或定宽容器的子元素的margin-right的定位失效。使用margin属性改变自身的位置,必须是和当前元素定位方向一样的margin属性,否则margin只能影响后面的元素或父元素。
  6. 鞭长莫及导致的margin无效。
  7. 内联特性导致的margin无效。

功勋卓著的border属性

为什么border-width不支持百分比值

border-width支持关键字,thinmedium

了解各种border-style类型

  1. border-style:none,默认值
  2. border-style:solid,实线边框
  3. border-style:dashed,虚线边框
  4. border-style:dotted,点线边框
  5. border-style:double,双线边框
  6. 其他border-style类型,inset内凹,outset外凸,groove沟槽,ridge山脊

border-colorcolor

border-color默认颜色就是color色值。

See the Pen 加号按钮 by whjin (@whjin) on CodePen.

border与透明边框技巧

1. 右下方background定位技巧

方法一:使用透明边框

.box {
    border: 50px solid transparent;
    background-position: 100% 50%;
}

2. 增加点击区域大小

3. 三角等图形绘制

.box {
    width: 0;
    border: 10px solid;
    border-color: #f30 transparent transparent;
}  
<div class="box"></div> 

border与图形构建

border等高布局技术

See the Pen border等高布局 by whjin (@whjin) on CodePen.

父容器不能使用overflow:hidden;清除浮动影响,因为溢出隐藏是基于padding box的,如果设置了则左浮动的导航列表元素就会被隐藏掉。