HTML 与 CSS

总结回顾面试中常见的 HTML 与 CSS 考点;

HTML

1. 如何理解 HTML 语义化

根据内容结构选择合适的标签,写出便于阅读与理解,同时利于搜索引擎优化的代码;

2. 为什么要语义化

  1. 在没有 CSS 的情况下也能呈现良好的网页内容结构与代码结构;
  2. 提高用户体验,如 title、alt 等解释属性的使用;
  3. 利于 SEO,因为爬虫依赖标签来确定上下文和各个关键字的权重;
  4. 方便其他设备解析,如盲人阅读器、屏幕阅读器等;
  5. 便于团队开发与维护,代码的可读性高,而且遵循标准对未来的兼容性也更好;

3. 如何做才能遵循语义化

  1. 减少使用无语义的标签,如 div、span、i、b 等;
  2. 不使用纯样式标签,改用 CSS,如 b、font、u 等;
  3. 需要强调的文本使用对应的标签,如加粗可以使用 strong 而不是 b,斜体使用 em 而不是 i;
  4. 为每个 input 标签添加对应的说明 label 标签,为 input 设置 id 属性,同时在 label 标签上使用 for=id 属性关联对应的 input;

4. HTML5 新增的语义化标签

  1. header 表示网页或 section 的页眉;
  2. footer 表示网页或 section 的页脚;
  3. nav 表示网页导航;
  4. section 表示网页中的一段或一节;
  5. article 表示表示网页中自成一体的内容;
  6. aside 表示侧边栏,通常放置与 article 相关的辅助信息;

新增的太多了,还有多媒体标签 audio、video,画图 canvas,日期 time,高亮显示 mark 等;

CSS

1. 块级元素与内联元素

块级元素

块级元素为display: [block | table];的元素;

常见的块级元素有 div、h1 ~ h6、ul、ol、li、table、p 等;

块级元素的特点

  1. 元素独占一行;
  2. 元素的宽高、内边距、边框、外边距都可以通过 CSS 控制;
  3. 元素默认的宽度为 auto,也就是父级元素的宽度;
  4. 元素内部可以放置其他块级元素或内联元素;
  5. p 是一个特殊的块级文本元素,内部只能放置文本或内联元素;

内联元素

内联元素为display: [inline | inline-block];的元素;

内联元素又分为行内元素和行内块元素;

行内元素

行内元素为display: inline;的元素,常见的行内元素有 span、a、strong、em、button 等;

行内元素的特点

  1. 相邻元素在同一行排列,直到触碰容器的边缘才会换行;
  2. 直接设置宽高无效;
  3. 只能设置水平方向的外边距;
  4. 默认宽度就是自身内容的宽度;
  5. 元素内部只能放置文本或其他行内元素;
行内块元素

行内块元素为display: inline-block;的元素,常见的行内块元素有 img、input 等;

行内块元素的特点

  1. 相邻元素在同一行排列,但之间有空白间隙;
  2. 宽高、内边距、边框、外边距也可以通过 CSS 控制;
  3. 默认宽度就是自身内容的宽度;

2. 盒模型

  1. 盒模型由 content、padding、border、margin 四部分组成;
  2. 盒模型分为标准盒模型与 IE 盒模型;
  3. 标准盒模型的宽高属性仅包含 content;
  4. IE 盒模型的宽高则包含 content、padding 和 border;
  5. 可以使用 CSS 的 box-sizing 属性切换盒模型,属性值 border-box 表示 IE 盒模型,content-box 表示标准盒模型;

盒模型的宽度计算

问题来了:

问题来了

首先,offsetWidth 是啥?

offsetWidth = content + padding + border 的宽度;

引申扩展一下:

clientWidth = content + padding 的宽度;

scrollWidth = content + padding 的宽度;

咦,clientWidth 和 scrollWidth 的宽度一样哎,是的,在内容没有溢出的情况下,它俩的计算方式是一样的;

但是,当内容溢出时(啥叫内容溢出,就是内容太多出现滚动条的时候),scrollWidth > clientWidth,scrollWidth 表示的是内容的实际宽度,而 clientWidth 则表示当前内容可视区域的宽度,二者的宽度都不包含滚动条的宽度;

举个例子 🌰:当前窗口可以显示 800px 宽度的内容,当内容宽度为 1000px 时,scrollWidth 获取的就是 1000px,而 clientWidth 为 800px,它不随内容宽度的变化而变化,只跟窗口的可视宽度有关;

现在来回答上面的问题:

回答上面的问题

再扩展一下,如果要让 box 的 offsetWidth 为 100,如何修改?

offsetWidth 的定义是不是和 IE 盒模型很像(当然了,这个标准就是微软提交的),既然要让盒模型的宽高包含 padding 和 border,只需要修改盒模型就可以了;

未修改

上图 👆box-sizing: content-box;,默认属性;
下图 👇box-sizing: border-box;

已修改

差距很直观吧;

3. margin

margin 纵向重叠

margin 纵向重叠

如上图,请问内容为AAA的 p 标签与内容为BBB的 p 标签之间的 margin 值是多少?

答案是 15px,原因有三:

  1. 相邻元素的 margin-top 和 margin-bottom 会发生重叠;
  2. margin 纵向重叠会取重叠区域的最大值;
  3. 空白内容也会发生重叠;

margin 负值

对 margin 的 top、left、right、bottom 设置负值,有何效果?

  1. top:元素自身向上移动,影响相邻元素向上移动;
  2. left:元素自身向左移动,影响相邻元素向左移动;
  3. right:元素自身不会移动,但 CSS 可读宽度减小,影响相邻元素向左移动;
  4. bottom:元素自身不会移动,但 CSS 可读高度减小,影响相邻元素向上移动;

4. BFC

Block Format Context,块级格式化上下文,一块独立渲染的区域,内部元素的渲染不会影响边界以外的其他元素;

触发 BFC 的条件

  1. 浮动元素,元素的 float 属性不为 none;
  2. 绝对定位元素,元素的 position 属性为 absolute 或 fixed;
  3. display 不为 visible 的元素;
  4. 行内块元素,元素的 display 为 inline-block;
  5. 弹性元素,display 为 flex 或 inline-flex 的元素的直接子元素;
  6. 网格元素,display 为 grid 或 inline-grid 的元素的直接子元素;
  7. display 为 flow-root 的元素;
  8. 表格,display 为 table、inline-table、table-cell 等的元素;

BFC 的应用场景

  1. 清除浮动;
  2. 父子元素 margin 重叠;

5. float 布局

圣杯布局和双飞翼布局一般用于 PC 端两侧内容固定,中间内容随着视口宽度自适应的三栏布局需求,且中间栏的主体优先加载和渲染,内容最重要嘛;

圣杯布局

圣杯布局出自Matthew Levine的文章In Search of the Holy Grail

首先是 dom 结构:

1
2
3
4
5
<div class="container">
<div class="center column">center</div>
<div class="left column">left</div>
<div class="right column">right</div>
</div>

可以看到内容主体center位于最前面,这样主体内容就到了文档流的最前面,实现了优先加载和渲染;

接着是 css 内容,首先是基础布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.container {
padding-left: 200px;
padding-right: 150px;
}
.container .column {
float: left;
height: 300px;
}
.center {
width: 100%;
background-color: #00f;
}
.left {
width: 200px;
background-color: #f00;
}
.right {
width: 150px;
background-color: #0f0;
}

这样就实现了如下图的布局,这里为了方便描述,给它们添加了固定高度和背景色;

如下图的布局

可以看到,由于 center 占据了父级元素 100% 的宽度,所以 left 和 right 只能被挤到下一行排列,同时这也是布局的精髓,正因为是被挤到下一行的,所以只使用 margin-left 负值就能让它们到达指定位置,妙啊;

然后就是将 left 和 right 分别移动到左边和右边,由于父元素有 padding,所以 left 和 right 会覆盖 center 对应宽度的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.container {
padding-left: 200px;
padding-right: 150px;
background-color: #ccc;
display: flow-root; /* BFC */
}
.left {
width: 200px;
background-color: #f00;
margin-left: -100%;
}
.right {
width: 150px;
background-color: #0f0;
margin-left: -150px;
}

这里为了查看效果,给父元素添加背景色,同时触发 BFC,使父元素高度不再塌陷;

效果如下 👇:

覆盖 center

最后,使用相对定位将 left 和 right 定位到指定位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.left {
position: relative;
width: 200px;
background-color: #f00;
margin-left: -100%;
right: 200px;
}
.right {
position: relative;
width: 150px;
background-color: #0f0;
margin-left: -150px;
left: 150px;
}

定位到指定位置

可以看到,center 被遮挡的内容也显示了,圣杯布局就完成了;

抱歉,还没有完,由于圣杯布局使用了定位,所以当视窗尺寸小于 left * 2 + right 的尺寸时,页面布局就会完全乱掉:

页面布局就会完全乱掉

这里为什么是 left * 2 呢,由于 left 使用了position: relative,所以就意味着在 center 区域中,还存在着一个 left 的宽度,所以页面的最小宽度应该是 left * 2 + right,也就是 200 * 2 + 100 = 550px;

去除无用设置,最终的 CSS 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.container {
min-width: 550px;
padding-left: 200px;
padding-right: 150px;
}
.container .column {
position: relative;
float: left;
}
.center {
width: 100%;
}
.left {
width: 200px;
margin-left: -100%;
right: 200px;
}
.right {
width: 150px;
margin-left: -150px;
left: 150px;
}

圣杯布局完成;

双飞翼布局

双飞翼布局始于淘宝 UED,据说出自玉伯之手,链接无;

首先是 dom 结构:

1
2
3
4
5
6
7
<div class="container">
<div class="center column">
<div class="main">center</div>
</div>
<div class="left column">left</div>
<div class="right column">right</div>
</div>

不同于圣杯布局,双飞翼布局多了一层 dom 结构;

然后是基础布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
.container {
display: flow-root;
background-color: #ccc;
}
.container .column {
float: left;
height: 300px;
}
.center {
width: 100%;
background-color: #00f;
}
.center .main {
height: 300px;
margin-left: 200px;
margin-right: 150px;
background-color: #f0f;
}
.left {
width: 200px;
background-color: #f00;
}
.right {
width: 150px;
background-color: #0f0;
}

同样,为了方便查看效果,添加诸多啰嗦的设置,如背景色,固定高度等;

双飞翼布局不再由父元素控制 center 的显示区域,而是添加一层 dom 结构,通过 margin 去预留 left 和 right 的空间,同时免除了使用 position 的隐患,效果如下 👇:

双飞翼布局

最后使用 margin-left 负值,将 left 和 right 放置到预留位置:

1
2
3
4
5
6
7
8
9
10
.left {
width: 200px;
background-color: #f00;
margin-left: -100%;
}
.right {
width: 150px;
background-color: #0f0;
margin-left: -150px;
}

布局完成:

放置到预留位置

总结与思考

圣杯布局与双飞翼布局各有优缺点,不过个人感觉双飞翼无论从布局的理解和布局的通用性,似乎更胜一筹,仁者见仁,智者见智。

不过这不是重点,重点是考点是什么?

首先,当然是 float 布局,还有清除浮动,BFC 以及 margin 负值等内容,这些都是基本功,一道题包含了这么多内容,是不是很有意思;

clearfix

清除浮动的方案有很多,不过随着自适应布局走向台前,用的不是很多了,这里就提两个常见的方案(要手写哦 😯);

  1. 伪元素:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    .clearfix::after {
    content: "";
    display: table;
    clear: both;
    }

    /* IE 兼容性 */
    .clearfix {
    *zoom: 1;
    }

    建议跟着规范走,伪元素使用双冒号,伪类使用单冒号;

  2. 触发 BFC,太常用,例子就不写了;

6. flex 布局

flex 布局已经很常见的,不会的童鞋赶紧去学习MDN

flex 布局中需要掌握的内容:

  1. flex-direction:主轴方向,默认 row;
  2. justify-content:主轴对齐方式;
  3. align-items:交叉轴对齐方式;
  4. align-self:元素自身在交叉轴的对齐方式,会覆盖 align-items 的设置;
  5. flex-wrap:是否换行;

以上属性是必须掌握的,下面的属性需要知道:

  1. flex-grow:元素放大,分配主轴剩余空间;

  2. flex-shrink:元素缩小;

  3. flex-basis:元素初始尺寸;

  4. flex 简写表示 flex-grow, flex-shrink, flex-basis;

    flex 简写值flex-growflex-shrinkflex-basis
    initial01auto
    auto11auto
    none00auto
    1110

用 flex 实现三点骰(tou)子

1
<div class="box"><span></span><span></span><span></span></div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.box {
width: 200px;
height: 200px;
background-color: #ccc;
border-radius: 30px;
padding: 20px;
display: flex;
justify-content: space-between;
}
.box span {
width: 50px;
height: 50px;
border-radius: 50%;
display: block;
background-color: #f00;
}
.box span:nth-child(2) {
align-self: center;
}
.box span:nth-child(3) {
align-self: flex-end;
}

效果如下:

三点骰(tou)子

很简单吧,只要了解 flex 的各个属性很容易写出来;

扩展 - 用 flex 实现三栏布局

浮动布局的缺点不言而喻,现在自适应布局使用的更多,动手尝试一下;

首先是 dom 结构,以圣杯布局的 dom 结构为例:

1
2
3
4
5
<div class="container">
<div class="center column">center</div>
<div class="left column">left</div>
<div class="right column">right</div>
</div>

然后是 CSS 设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.container {
display: flex;
}
.container .column {
height: 200px;
}
.center {
flex: 1;
background-color: #00f;
}
.left {
flex: 0 0 200px;
order: -1;
background-color: #f00;
}
.right {
flex: 0 0 150px;
background-color: #0f0;
}

使用 flex-basis 指定 left 和 right 的初始尺寸,然后用 order 将 left 放置在左侧;

7. 定位

  1. absolute 和 relative 分别依据什么定位?
  2. 居中对齐的实现方式

这里就不重复说明了,居中对齐请参考我的另一篇博客用 CSS 实现元素水平垂直居中

8. line-height 的继承

line-height 的继承分为三种情况:

  1. 具体的数值,子元素直接继承该数值;

    具体的数值

  2. 比例,子元素直接继承该比例;

    比例

  3. 百分比(坑在这里),先根据父元素的 font-size 和百分比计算出具体的数值,然后被子元素继承;

    百分比

    考点就在百分比这里,是先计算后继承,而不是直接继承;

9. 响应式

  1. rem 是一个相对长度单位,相对于根元素 html,任何使用长度的地方都可以使用;

  2. em 是一个相对长度单位,相对于父元素;

  3. px 是一个绝对长度单位;

响应式布局的方案

  1. 媒体查询(media-query),根据不同的屏幕宽度区间设置根元素的 font-size,然后使用 rem,缺点是粒度较大,呈阶梯状;
  2. vw / wh / vmax / vmin,根据浏览器视口的宽度或高度(包含滚动条)更细粒度的适配,更丝滑,不兼容低版本浏览器;