TML5是以HTML4为基础的,并对HTML4进行了大量的修改。
首先看看语法上的改变、
对于HTML4而言,HTML5在语法上发生了很大的变化。对于这些变化,有些人开始有点不安,是不是又要开始重新学习HTML语言了?但是,HTML5中的语法变化,与其他开发语言中的语法变化有根本的不同。因为在HTML5之前几乎没有符合标准规范的web浏览器导致的。
HTML的语法是在SGML语言的基础上建立起来的。但是SGML语法非常之复杂,要开发能够解析SGML语法的程序也很不容易,因此很多浏览器都不包含SGML的分析器。因此,虽然HTML基本上遵从SGML的语法,但是对于HTML的执行在各浏览器之间并没有一个统一的标准。
为解决各浏览器之间的互兼容性和互操作性,就得要有一个统一的标准,所以HTML5就是围绕这个web标准,重新定义了一套在现有HTML的基础上修改而来的语法,以便各浏览器在运行HTML的时候能够符合一个通用标准。
作为一名Web开发者,可能你并没有对这个“H5”这个字眼投入太多的关注,但实际上它早已不知不觉进入到你的开发中,并且总有一天会让你不得不正视它,了解它并运用它
打个比方:《海贼王》中的主角路飞在“顶上战争两年前”,会在一些危急关头“不经意”地使用霸王色霸气,但对”霸气“的结构体系和具体运用都不太了解,这让他在香波地群岛等诸多重大战役中大吃苦头。此后, 他不惜花费两年时间跟随雷利修炼霸气。因为,如果不去了解这个崭新的战斗方法的话,他们在残酷的新世界一天也生存不了。
咳咳, 回到主题,为什么我们要学HTML5呢?
1. 了解HTML5的囊括范围的一大好处是:当你不小心使用了一个H5的东东的时候(例如你试图通过百度找到的答案解决一个紧张的需求),你会很及时的关注它的兼容性
2. H5有些新增的特性也许你从没接触过,也感觉无需用到它。但就在不久的将来,你可能就会用到,甚至依赖于它(毕竟这就是HTML的未来)
在下面, 我将学习H5中的知识点分成两类:主要知识点和针对特定功能的知识点,其中对主要知识点的部分,从学习成本的角度对其进行了难度分级
(仅属个人观点!如有改进意见,欢迎讨论)
一.主要知识点
(从需求层面上来说,普及范围相对较广)
相对容易的部分:
1.在线和离线事件(Online/Offline) (相对容易)
2. 众多的新增元素 如<output>, <progress>等 (相对容易)
3. history关于历史状态管理的API (相对容易)
4 Storage(localStorage和sessionStorage) (相对容易)
相对较难的部分:
5. Web Worker (相对较难)
6. canvas (相对较难)
7. indexedDB (相对较难)
8. 拖放操作 (相对较难)
9. Web Sockets (相对较难)
二. 针对特定功能的知识点
(对需求来说,主要针对某一方面的特殊需求场景)
1. 对音视频的支持
2. Camera API (操作摄像头)
3. WebGL (3D图像)
4. 地理位置定位 (geolocation对象)
本文主要讲述H5中主要知识点中,学习成本相对较高的四个点(仅个人观点):
一.Web Worker
二.canvas
三.indexedDB
四.拖放操作
【注意】因为下面介绍的H5的特性在一些比较老的浏览器里可能遇到兼容性问题,所以你在使用前必须要能力检测,例如这样
Web Worker的机制让你能够创建一个在后台线程运行的脚本,这个脚本不会对我们当前执行任务的脚本造成任何干扰(例如阻塞),同时Web Worker提供了一套API使你能够在当前脚本和后台脚本间进行数据的互相传输(worker)
“一套API, 两个对象”
我们现在已知的关于Web Worker的机制是: 有一个当前脚本, 和一个在后台运行的worker脚本,所以我们问题的关键就落在了这两个脚本的通信(数据交互)上
通过
生成了“两个对象”(你可能会问:为什么是两个不是一个呢?请往下看)
“第一个”对象是我们在当前脚本中通过构造函数显式创建出来的worker对象,它拥有一套API:postMessage和onmessage,通过postMessage方法可以向worker脚本(上文worker.js)发送数据, 通过onmessage方法可以从worker脚本接收数据
“第二个”对象是在Web Worker脚本(上文的worker.js)中隐式创建出来的全局变量对象,它叫DedicatedWorkerGlobalScope(这个时候在work.js全局变量对象是它而不是Window!!),而它也拥有一套API:postMessage和onmessage,通过postMessage方法可以向当前执行任务的脚本发送数据, 通过onmessage方法可以从当前执行任务的脚本接收数据
【注意】关于DedicatedWorkerGlobalScope
1. 它是在Web Worker脚本中生成的特殊的全局变量对象,也就是在全局执行环境中使用this指向的不是Window而是它
2. 它不能像Windows那样通过变量名直接访问,但在Web Worker脚本中你能通过this取到它
所以现在数据传递方向有两条:
1. 调用当前脚本中worker对象的postMessage方法, 然后在Web Worker脚本(上文的worker.js)中通过onmessage这个回调方法接收数据
2. 调用Web Worker脚本中的this.postMessage方法(this指向DedicatedWorkerGlobalScope),然后在当前脚本中worker对象的onmessage回调方法接收数据
看到这里可能有点懵,来让我们通过一个例子看看1中的数据传递:
先看示例吧,这是我们的目录结构
index.html:
main.js:
worker.js:
点击按钮后,在main.js中调用worker对象的postMessage方法, 这个数据就被发给了work.js中的全局变量对象DedicatedWorkerGlobalScope, 所以我们在work,js中通过this.onmessage接收数据并输出
【注意】postMessage传递的参数会被“原封不动”地传递给onmessage中event对象的data属性
例如:
postMessage([1,2,3]) ——> this.onmessage = function (e) { } 中 e.data === [1,2,3]
postMessage({a:1,b: 2}) ——> this.onmessage = function (e) { } 中 e.data === {a:1,b: 2}
我们上面的例子展现的是从当前任务脚本向worker脚本传递数据,那么同样的道理,我们也能从work脚本向当前任务脚本传递数据(方式相同)
例子:
index.html:
同上
main.js:
worker.js:
demo如下
点击传递数据输出:
cancas是H5新增的一个标签,把canvas翻译过来就是画布,顾名思义,这是用来”画画的“,那画画的”画笔“是什么呢? 它就是和canvas元素对象对应的一个”上下文对象“(context),这里的这个上下文对象可能和你印象中的”上下文“有较大的差异,它只是个单纯的包含了一系列“绘画”方法的对象,下面我们介绍的关于canvas的内容都要围绕这个"canvas上下文对象"展开
我们可以通过这种方式取得canvas上下文对象:
假设这是我们的HTML:
这样取得上下文对象:
下面展现的是上下文对象的一些绘制图形的方法(它们都可以被ctx调用)
上面的x,y代表相对于canvas画布左上角的横纵坐标:
例子:
html部分:
JS部分:
【注意】. canvas标签内的内容(例如上面的文本)是否呈现取决于浏览器是否支持canvas,如果支持,则不出现,如果不支持,则会呈现出来
demo:
我们以上面的为基础稍作修改:
demo:
demo:
这里要稍微提一下, 也许上面的那些绘制图形,绘制文本的操作对你来说都没有触动,因为它们离我们的直接需求似乎还有一定的距离,但我想接下来的这几个上下文API你或许有些兴趣。
例如我们可能有一个需求是载入已有的图片,对它截图(裁剪)后保存为一张新的图片,这个时候我们就可以使用到canvas的绘制图片,裁剪图片,保存图片的API了
通过canvas上下文对象的drawImage方法可直接绘制图片
我们可以通过下面的一段代码动态获取img元素对象
废话不多说,直接上demo!
在相同目录下有这么一张图片
JS代码:
demo:
我们发现, 图片加载完成后被写入了画布当中!
canvas上下文对象的clip方法可根据路径对canvas画布进行裁剪
让我们在原来的基础上添加一点东西:
【注意】clip方法的调用要在drawImage方法之前,否则不能成功! 也就是说要“先裁剪,再画图”
canvas的保存和导出
我们通过document.getElementById("canvas")取得的画布对象,有一个toDataURL()方法,可将当前画布作为一张图片,并返回其base64编码格式的数据,这在保存图片的时候非常有用
toDataURL接受两个参数:图片类型和质量参数
canvas.toDataURL(图片类型,质量参数)
看下面的例子
控制台输出了base64格式的数据:
我们通过网上的还原软件看看会把这个base64数据还原成什么图片:
正是我们想要的图片
indexedDB是存在于浏览器中的数据库,它和一般的数据库一样有写改删查的功能,不同之处在于:常见的数据库一般是在服务器上,并且要求我们的应用在线时才可以工作,而indexedDB使得在离线的时候读取数据成为了可能。下面,我就给大家介绍一下这个“驻扎”在浏览器上的特殊的数据库吧
我们首先要做的事情,当然是创建(或打开)一个数据库,这要用到indexedDB对象的open方法
它接收两个参数: 数据库名称和数据库版本(第二个参数是可选的)
调用open方法时候,如果对应名称的数据库不存在,则创建一个新的数据库,如果已存在,则打开已存在的那个数据库
需要说明的是, indexedDB里面绝大多数操作都是异步的, 上述的indexedDB.open并不会立即创建一个数据库, 你需要在异步的回调里面判断数据库是否创建成功,并对可能出现的错误做判断和处理
只有在onsuccess回调中,你才能通过request.result取得创建成功的数据库
通过open返回的request对象有三个回调:
onsuccess 每次创建/打开数据库时候都会调用
onerror 创建/打开数据库发生错误的时候调用
onupgradeneeded 数据库版本变化的时候调用 (onupgradeneeded 是我们唯一可以修改数据库结构的地方)
open一个indexedDB数据库后,一般在onupgradeneeded回调中初始化(或修改)数据库结构(划重点!!)
这包括两个方面的操作:
1. 通过db.createObjectStore创建对象存储空间,并取得ObjectStore对象(类似于SQL数据库中的建表操作)
2. 通过调用ObjectStore.createIndex创建该存储空间内的索引( 以便于提高查询时候的速度)
具体的可看下面的例子:
运行一下, 然后让我们看看效果:
打开chrome的Application面板,点击左栏的Storage下的indexedDB使其展开
就可以看到我们新创建的phwDataBase数据库, 以及它内部的people数据存储空间了
(右边展示的是people数据存储空间的具体内容,因为现在什么数据都还没添加,所以key和value两列下是没有内容的)
看了上面的代码你可能会有些疑惑
onupgradeneeded 和onsuccess回调的关系是怎样的? 为什么我们必须在.onupgradeneeded中初始化数据库的结构,而不是在onsuccess中?
这主要是由两个回调调用的时机决定的:
1.对 onsuccess回调,在每次数据库创建/打开的时候都会调用(不仅是第一次创建的时候会调用,每次打开的时候也都会调用)
2. 对onupgradeneeded回调,在open提供第二个版本参数的前提下:
2.1 第一次调用open方法创建一个新的数据库的时候,onupgradeneeded一定会被调用
2.2 第二次或以后open该数据库,只在版本参数改变的时候, onupgradeneeded才会被调用
【注意】在缺少第二个版本参数的情况下,onupgradeneeded永远不会被调用!!
所以说:
1.open数据库的时候,最(yi)好(ding)要带上第二个参数(版本参数)
2. 修改数据库结构(例如创建和删除对象存储空间以及构建和删除索引。)要在onupgradeneeded回调中运行
(很显然每次打开都会被调用的onsuccess并不适合用于初始化数据库结构)
首先说一下,在下面的展示例子中,我们的HTML是这样的
demo:
这里要说明一下的是,indexedDB的操作是以事务为基础的。 所以,对存储空间(objectStore)的操作都要基于事务来进行。 具体点说,就是需要先通过db.transaction()方法取得transaction对象,然后再通过transaction.objectStore()方法取得目标objectStore,再然后才能调用objectStore的API进行“写改删查”
打个比方, 如果说我们存储的数据是粮食的话, objectStore就是一个个并排的粮仓,你可以往里面运粮食,也可以把粮食运出去, 但你对“粮食”做任何行为前, 都要和粮仓门前的守卫—— transaction(事务)“打声招呼”,得到准许才能进入粮仓
有两个方法要说一下
1. transaction方法
transaction 方法 一般接受两个参数,并返回一个事务对象。
1.1第一个参数是一个数组, 一个我们希望事务能够操作的objectStore所组成的数组,如果你希望这个事务能够操作所有的objectStore,那么传入空数组[]即可
1.2 第二个参数是一个字符串, 默认是“onlyread”, 如果我们有需要对数据进行写操作的需求的话可传入“readwrite”
例如我们下面的一行代码:
2. transaction.objectStore方法
这个方法接受一个参数: 指定的objectStore的名称, 方法返回的是获取到的objectStore
例如我们下面的一行代码:
写操作的关键在于objectStore.add(XXX);方法,其中XXX是我们初始化objectStore时候写入的“主键”
也就是 var objectStore = db.createObjectStore("people", { keyPath: "id" }); (这段代码在上面)中keyPath的值——id
demo:
点击“增加数据”后弹出
再看看application面板下的indexedDB:
我们已经成功添加了三条数据进去了
删操作的关键在于objectStore.delete(XXX);方法,其中XXX是我们初始化objectStore时候写入的“主键”
也就是 var objectStore = db.createObjectStore("people", { keyPath: "id" }); 中keyPath的值——id
点击上面的“删除数据”按钮(删除id = 1的数据)
再来看看, id为1的那一行已经被删除了
删操作的关键在于objectStore.get(XXX);方法
demo:
点击“获取数据”按钮,弹出
(这里固定查找id为2的数据)
遍历数据需要用到游标
通过 objectStore.openCursor()可创建一个游标对象(cursor), 这个cursor对象包含两个属性值: key和value
key就是我们一直说的那个“主键”, 而value是我们存入的时候的那个对象,通过 cursor.continue方法可以使得游标向下移动
点击“遍历全部数据”按钮,看看控制台
我们通过objectStore.get方法,通过查找主键的方式查找对应的对象数据的方式是很快的。
但如果我们通过非主键的数据去查找对应的那个对象就非常慢了,这个时候我们需要创建一个索引并通过索引来查找, 从而获得较快的速度:
点击“通过索引获取数据”按钮:
好! 现在让我们对indexedDB做一个小小的总结:
1. indexedDB是面向对象的, 与传统的以二维表为基础的数据库不同
2. IndexedDB是一个事务型数据库系统
3. indexedDB大多数API都是异步的,这意味着调用一个方法你不能马上得到关键的那个对象,而在对应的success回调中才能取得
一个典型的拖放操作是这样开始的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一系列的拖放类型的事件
其中我们主要关心的事件有三个:
1. ondragstart 发生在可拖拽(draggable)的元素上, 在元素被拖动的时候调用
2. ondragover 发生在可放置(droppable)的元素上, 当某被拖动的对象在可放置对象范围内(上方)时触发此事件
3. ondrop 发生在可放置(droppable)的元素上,当释放鼠标使可拖拽元素“放进”可放置元素内的瞬间触发。
需思考的问题:
1. 如何使得被拖拽元素可拖拽?(因为元素默认的行为是不可拖拽的),以及如何使得被放置的容器元素可放置? (因为元素默认是不可放置的)
对前者, 我们可以为元素设置draggable属性,并且设置为true
对后者, 我们可以在被放置的容器元素中的ondragover事件里通过event.preventDefault();阻止默认行为——禁止放置
2.如何实现“脱 — 放”过程的数据传递?
这里首先需要知道的是,当我们拖动一个图片到另一个地方的时候,我们是不能“直接把图片拖拽进去”的,也就是说,我们还是要通过以下的思路实现拖放:
在被放置的元素中取得被拖拽元素的相关数据(如id),然后通过appendChild之类的API实现添加被拖拽的元素,从而模拟整个拖拽的过程
也就是说, 拖拽其实可分为三个过程: 拖动—传递被拖动元素的数据(如id)—在容器元素中添加该元素
关键在于如何在被拖动元素和被放置元素中传递数据,这可以通过event.dataTransfer对象来实现
dataTransfer可以通过setData方法添加拖动数据,并通过getDate方法取得拖动数据,我们可以在
ondragstart事件和ondrop事件中调用这两个方法, 实现关键性的数据传递。
具体请看下面的例子:
拖拽前
拖拽后
参考资料:
HTML5-MDN https://developer.mozilla.org/zh-CN/docs/Web/Guide/HTML/HTML5
【完】
CSS 选择器有很多,不同的选择器的权重和优先级不一样,对于一个元素,如果存在多个选择器,那么就需要根据权重来计算其优先级。
权重分为四级,分别是:
需要注意的是:通用选择器(\*)、子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,所以他们的权值都为 0。 权重值大的选择器其优先级也高,相同权重的优先级又遵循后定义覆盖前面定义的情况。
1. 什么是“盒子”
初学 CSS 的朋友,一开始学 CSS 基础知识的时候一定学过padding border和margin,即内边距、边框和外边距。它们三者就构成了一个“盒子”。就像我们收到的快递,本来买了一部小小的手机,收到的却是那么大一个盒子。因为手机白色的包装盒和手机机器之间有间隔层(内边距),手机白色盒子有厚度,虽然很薄(边框),盒子和快递箱子之间还有一层泡沫板(外边距)。这就是一个典型的盒子。
如上图,真正的内容就是这些文字,文字外围有 10px 的内边距,5px 的边框,10px 的外边距。看到盒子了吧?
题目:盒子模型的宽度如何计算
2. 固定宽度的盒子
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px;">
文章言简意赅的介绍的浏览器的工作过程,web前端
</div>
如上图,得到网页效果之后,我们可以用截图工具来量一下文字内容的宽度。发现,文字内容的宽度刚好是 300px,也就是我们设置的宽度。
因此,在盒子模型中,我们设置的宽度都是内容宽度,不是整个盒子的宽度。而整个盒子的宽度是:(内容宽度 + border宽度 + padding宽度 + margin宽度)之和。这样我们改四个中的其中一个,都会导致盒子宽度的改变。这对我们来说不友好。
没关系,这个东西不友好早就有人发现了,而且已经解决,下文再说。
3. 充满父容器的盒子
默认情况下,div是display:block,宽度会充满整个父容器。如下图:
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px;">
之前看过一篇文章,叫做《浏览器工作原理:新式网络浏览器幕后揭秘》,
文章言简意赅的介绍的浏览器的工作过程,web前端
之前看过一篇文章,叫做《浏览器工作原理:新式网络浏览器幕后揭秘》,
文章言简意赅的介绍的浏览器的工作过程,web前端
</div>
4. 包裹内容的盒子
这种情况下比较简单,内容的宽度按照内容计算,盒子的宽度将在内容宽度的基础上再增加(padding宽度 + border宽度 + margin宽度)之和。
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px;">
之前看过一篇文章,叫做《浏览器工作原理:新式网络浏览器幕后揭秘》
</div>
5. box-sizing:border-box
前面提到,为盒子模型设置宽度,结果只是设置了内容的宽度,这个不合理。如何解决这一问题?答案就是为盒子指定样式:box-sizing:border-box。
<div style="padding:10px; border:5px solid blue; margin: 10px; width:300px; box-sizing:border-box;">
之前看过一篇文章,叫做《浏览器工作原理:新式网络浏览器幕后揭秘》
</div>
上图中,为div设置了box-sizing:border-box之后,300px 的宽度是内容 + padding + 边框的宽度(不包括margin),这样就比较符合我们的实际要求了。建议大家在为系统写 CSS 时候,第一个样式是:
* {
box-sizing:border-box;
}
大名鼎鼎的 Bootstrap 也把box-sizing:border-box加入到它的*选择器中,我们为什么不这样做呢?
6. 纵向 margin 重叠
这里提到 margin,就不得不提一下 margin 的这一特性——纵向重叠。如<p>的纵向 margin 是 16px,那么两个<p>之间纵向的距离是多少?—— 按常理来说应该是 16 + 16 = 32px,但是答案仍然是 16px。因为纵向的 margin 是会重叠的,如果两者不一样大的话,大的会把小的“吃掉”。
float 用于网页布局比较多,使用起来也比较简单,这里总结了一些比较重要、需要注意的知识点,供大家参考。
1. 误解和误用
float 被设计出来的初衷是用于文字环绕效果,即一个图片一段文字,图片float:left之后,文字会环绕图片。
<div>
<img src="image/1.png" style="float:left">
一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字一段文字
</div>
但是,后来大家发现结合float + div可以实现之前通过table实现的网页布局,因此就被“误用”于网页布局了。
题目:为何 float 会导致父元素塌陷?
2. 破坏性
float 的破坏性 —— float 破坏了父标签的原本结构,使得父标签出现了坍塌现象。导致这一现象的最根本原因在于:被设置了 float 的元素会脱离文档流。其根本原因在于 float 的设计初衷是解决文字环绕图片的问题。大家要记住 float 的这个影响。
3. 包裹性
包裹性也是 float 的一个非常重要的特性,大家用 float 时一定要熟知这一特性。咱们还是先从一个小例子看起:
如上图,普通的 div 如果没有设置宽度,它会撑满整个屏幕,在之前的盒子模型那一节也讲到过。而如果给 div 增加float:left之后,它突然变得紧凑了,宽度发生了变化,把内容中的三个字包裹了——这就是包裹性。为 div 设置了 float 之后,其宽度会自动调整为包裹住内容宽度,而不是撑满整个父容器。
4. 清空格
float 还有一个大家可能不是很熟悉的特性——清空格。按照惯例,咱还是先举例子说明。
<div style="border: 2px solid blue; padding:3px;">
<img src="image/1.png"/>
<img src="image/2.png"/>
<img src="image/3.png"/>
<img src="image/4.png"/>
</div>
加上float:left之后:
题目:手写 clearfix
5. clearfix
清除浮动的影响,一般使用的样式如下,统称clearfix代码。所有 float 元素的父容器,一般情况下都应该加clearfix这个 class。
.clearfix:after {
content: '';
display: table;
clear: both;
}
.clearfix {
*zoom: 1; /* 兼容 IE 低版本 */
}
<div class="clearfix">
<img src="image/1.png" style="float: left"/>
<img src="image/2.png" style="float: left"/>
</div>
6. 小结
float 的设计初衷是解决文字环绕图片的问题,后来误打误撞用于做布局,因此有许多不合适或者需要注意的地方,上文基本都讲到了需要的知识点。如果是刚开始接触 float 的同学,学完上面的基础知识之后,还应该做一些练习实战一下 —— 经典的“圣杯布局”和“双飞翼布局”。这里就不再展开讲了,网上资料非常多,例如浅谈面试中常考的两种经典布局——圣杯与双飞翼(此文的最后两张图清晰地展示了这两种布局)。
position 用于网页元素的定位,可设置 static/relative/absolute/fixed 这些值,其中 static 是默认值,不用介绍。
题目:relative 和 absolute 有何区别?
1. relative
相对定位 relative 可以用一个例子很轻松地演示出来。例如我们写 4 个<p>,出来的样子大家不用看也能知道。
<p>第一段文字</p>
<p>第二段文字</p>
<p>第三段文字</p>
<p>第四段文字</p>
然后我们在第三个<p>上面,加上position:relative并且设置left和top值,看这个<p>有什么变化。
<p>第一段文字</p>
<p>第二段文字</p>
<p style="position:relative; top: 10px; left: 10px">第三段文字</p>
<p>第四段文字</p>
上图中,大家应该要识别出两个信息(相信大部分人会忽略第二个信息)
可见,relative 会导致自身位置的相对变化,而不会影响其他元素的位置、大小。这是 relative 的要点之一。还有第二个要点,就是 relative 产生一个新的定位上下文。下文有关于定位上下文的详细介绍,这里可以先通过一个例子来展示一下区别:
注意看这两图的区别,下文将有解释。
2. absolute
还是先写一个基本的 demo。
<p>第一段文字</p>
<p>第二段文字</p>
<p style="background: yellow">第三段文字</p>
<p>第四段文字</p>
然后,我们把第三个<p>改为position:absolute;,看看会发生什么变化。
从上面的结果中,我们能看出几点信息:
最后,通过给 absolute元素设置 top、left 值,可自定义其内容,这个都是平时比较常用的了。这里需要注意的是,设置了 top、left 值时,元素是相对于最近的定位上下文来定位的,而不是相对于浏览器定位。
3. fixed
其实 fixed 和 absolute 是一样的,唯一的区别在于:absolute 元素是根据最近的定位上下文确定位置,而 fixed 根据 window (或者 iframe)确定位置。
题目:relative、absolute 和 fixed 分别依据谁来定位?
4. 定位上下文
relative 元素的定位永远是相对于元素自身位置的,和其他元素没关系,也不会影响其他元素。
fixed 元素的定位是相对于 window (或者 iframe)边界的,和其他元素没有关系。但是它具有破坏性,会导致其他元素位置的变化。
absolute 的定位相对于前两者要复杂许多。如果为 absolute 设置了 top、left,浏览器会根据什么去确定它的纵向和横向的偏移量呢?答案是浏览器会递归查找该元素的所有父元素,如果找到一个设置了position:relative/absolute/fixed的元素,就以该元素为基准定位,如果没找到,就以浏览器边界定位。如下两个图所示:
布局的传统解决方案基于盒子模型,依赖 display 属性 + position 属性 + float 属性。它对于那些特殊布局非常不方便,比如,垂直居中(下文会专门讲解)就不容易实现。在目前主流的移动端页面中,使用 flex 布局能更好地完成需求,因此 flex 布局的知识是必须要掌握的。
1. 基本使用
任何一个容器都可以使用 flex 布局,代码也很简单。
<style type="text/css">
.container {
display: flex;
}
.item {
border: 1px solid #000;
flex: 1;
}
</style>
<div class="container">
<div class="item">aaa</div>
<div class="item" style="flex: 2">bbb</div>
<div class="item">ccc</div>
<div class="item">ddd</div>
</div>
注意,第三个<div>的flex: 2,其他的<div>的flex: 1,这样第二个<div>的宽度就是其他的<div>的两倍。
2. 设计原理
设置了display: flex的元素,我们称为“容器”(flex container),其所有的子节点我们称为“成员”(flex item)。容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做 main start,结束位置叫做 main end;交叉轴的开始位置叫做 cross start,结束位置叫做cross end。项目默认沿主轴排列。单个项目占据的主轴空间叫做 main size,占据的交叉轴空间叫做 cross size。
将以上文字和图片结合起来,再详细看一遍,这样就能理解 flex 的设计原理,才能更好地实际使用。
3. 设置主轴的方向
flex-direction可决定主轴的方向,有四个可选值:
.box {
flex-direction: column-reverse| column | row | row-reverse;
}
以上代码设置的主轴方向,将依次对应下图:
4. 设置主轴的对齐方式
justify-content属性定义了项目在主轴上的对齐方式,值如下:
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
5. 交叉轴的对齐方式
align-items属性定义项目在交叉轴上如何对齐,值如下:
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
题目:如何实现水平居中?
1. 水平居中
inline 元素用text-align: center;即可,如下:
.container {
text-align: center;
}
block 元素可使用margin: auto;,PC 时代的很多网站都这么搞。
.container {
text-align: center;
}
.item {
width: 1000px;
margin: auto;
}
绝对定位元素可结合left和margin实现,但是必须知道宽度。
.container {
position: relative;
width: 500px;
}
.item {
width: 300px;
height: 100px;
position: absolute;
left: 50%;
margin: -150px;
}
题目:如何实现垂直居中?
2. 垂直居中
inline 元素可设置line-height的值等于height值,如单行文字垂直居中:
.container {
height: 50px;
line-height: 50px;
}
绝对定位元素,可结合left和margin实现,但是必须知道尺寸。
.container {
position: relative;
height: 200px;
}
.item {
width: 80px;
height: 40px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -20px;
margin-left: -40px;
}
绝对定位可结合transform实现居中。
.container {
position: relative;
height: 200px;
}
.item {
width: 80px;
height: 40px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: blue;
}
绝对定位结合margin: auto,不需要提前知道尺寸,兼容性好。
.container {
position: relative;
height: 300px;
}
.item {
width: 100px;
height: 50px;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
}
其他的解决方案还有,不过没必要掌握太多,能说出上文的这几个解决方案即可。
题目:如何理解 HTML 语义化?
所谓“语义”就是为了更易读懂,这要分两部分:
1. 让人更易读懂
2. 让机器更易读懂
CSS3 可以实现动画,代替原来的 Flash 和 JavaScript 方案。
首先,使用@keyframes定义一个动画,名称为testAnimation,如下代码,通过百分比来设置不同的 CSS 样式,规定动画的变化。所有的动画变化都可以这么定义出来。
@keyframes testAnimation
{
0% {background: red; left:0; top:0;}
25% {background: yellow; left:200px; top:0;}
50% {background: blue; left:200px; top:200px;}
75% {background: green; left:0; top:200px;}
100% {background: red; left:0; top:0;}
}
后,针对一个 CSS 选择器来设置动画,例如针对div元素设置动画,如下:
div {
width: 100px;
height: 50px;
position: absolute;
animation-name: myfirst;
animation-duration: 5s;
}
animation-name对应到动画名称,animation-duration是动画时长,还有其他属性:
题目:CSS 的transition和animation有何区别?
首先transition和animation都可以做动效,从语义上来理解,transition是过渡,由一个状态过渡到另一个状态,比如高度100px过渡到200px;而animation是动画,即更专业做动效的,animation有帧的概念,可以设置关键帧keyframe,一个动画可以由多个关键帧多个状态过渡组成,另外animation也包含上面提到的多个属性。
重绘和回流是面试题经常考的题目,也是性能优化当中应该注意的点,下面笔者简单介绍下。
相比之下,回流要比重绘消耗性能开支更大。另外,一些属性的读取也会引起回流,比如读取某个 DOM 的高度和宽度,或者使用getComputedStyle方法。在写代码的时候要避免回流和重绘。比如在笔试中可能会遇见下面的题目:
题目:找出下面代码的优化点,并且优化它
var data = ['string1', 'string2', 'string3'];
for(var i = 0; i < data.length; i++){
var dom = document.getElementById('list');
dom.innerHTML += '<li>' + data[i] + '</li>';
}
上面的代码在循环中每次都获取dom,然后对其内部的 HTML 进行累加li,每次都会操作 DOM 结构,可以改成使用documentFragment或者先遍历组成 HTML 的字符串,最后操作一次innerHTML。
本小节总结了 CSS 和 HTML 常考的知识点,包括 CSS 中比较重要的定位、布局的知识,也介绍了一些 CSS3 的知识点概念和题目,以及 HTML 的语义化。
*请认真填写需求信息,我们会在24小时内与您取得联系。