HTML补档
图片的相对路径
图片的前缀不再使用固定的 url,可以加/
给图片地址.就会自动在前面拼接上 url 的 origin 地址前缀.
1 | //例如 |
如果存在网关,则需要加上网关.
export 和 export default 区别
- 在一个文件或模块中,export、import 可以有多个,export default 仅有一个
- 通过 export 方式导出,在导入时要加{ },export default 则不需要
1 | //a.js |
1 | // b.js |
defer 和 async 区别
区别主要在于一个执行时间,script 的执行时机.
两个网络请求都是异步的,不会阻塞浏览器解析 HTML.加载是都会加载,执行就有区别了.
defer
会在 dom 解析完之后执行,并且多个defer
会按照顺序执行,async
则是在 js 加载好之后就会执行脚本,并且多个async
,哪个加载好就执行哪个
解析:
在没有 defer 或者 async 的情况下:会立即执行脚本,所以通常建议把 script 放在 body 最后<script src="script.js"></script>
async:有 async 的话,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。
但是多个 js 文件的加载顺序不会按照书写顺序进行<script async src="script.js"></script>
defer:有 defer 的话,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成,并且多个 defer 会按照顺序进行加载。<script defer src="script.js"></script>
link 和 @import 区别
作用: 样式的导入方式
- 引入内容不同.
link
除了引入样式还可以引入图片等文件,@import
只能引入 css. - 加载顺序不同.
link
在页面加载时同时加载,@import
需要等页面加载完. - 兼容性不同.
link
是XHTML
标签,无兼容性问题.@import
低版本浏览器有可能不支持. - 对 JS 支持不同.
link
支持用 js 控制 DOM 去改变样式.@import
不支持.
为什么 link 用 href 获取资源,而 script 和 img 用 src?
link 用于在当前文档和引用资源建立联系,
src 用于替换当前元素.
1 | <link href="common.css" rel="stylesheet" /> |
在文档中添加 link 标签,浏览器会识别该文档为 css 文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用 link 方式来加载 css,而不是使用@import 方式
1 | <script src="a.js"></script> |
当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架 等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么将 js 脚本放在底部而不是头部
图片懒加载和预加载
懒加载: 延迟加载图片,达到加载条件时再加载。
原理:img 的 src 属性初始不放置实际需要的链接,而是存在 data-url 属性中,达到某条件后再动态充值 src 属性值为 data-url 里的链接地址,实现懒加载。
作用:减轻服务器压力,节约流量,页面加载速度快。
预加载:提前加载图片,当用户需要查看时是直接从浏览器缓存里取的资源,用户操作体验速度快。
原理:在浏览器加载页面时创建 image 对象并赋值 src 链接
作用:让用户有快速的冲浪体验。
缺点:增加服务器压力,首页加载渲染时间长。
区别: 懒加载与预加载都有提高用于体验效果,但是优化体验效果的出发点不同,懒加载可以减轻服务器压力,但是预加载会增加服务器压力;
懒加载
预加载
浏览器渲染
浏览器解析 => 加载
DOM 树构建
CSS 树构建
渲染树构建
domTree + cssTree => renderTree 构建完毕 => 浏览器根据它绘制页面
- 渲染树每个节点都有自己的样式
- 不包含 display:none,以及 head 之类不需要绘制的节点
- 渲染树每一个节点都被当做一个盒子,具有内容填充,边距,边框,位置,大小等其他样式
回流重绘
回流:reflow,一定引起重绘。
重绘:repaint。不一定是回流产生的。
回流
因为节点的尺寸,布局,display:none/block.
上面这些改变时,渲染树中的一部分或者全部需要重新构建
引起回流的操作:
- DOM 节点的增删,位置变化
- 元素的尺寸,边距,填充,边框,宽高
- DOM 节点 display 显示与否
- 页面渲染初始化
- 浏览器窗口尺寸变化
- 向浏览器请求某些样式(offset,scroll,client,width,height,getComputedStyle()
重绘
回流后,浏览器根据新的渲染树重新绘制回流影响的部分或者全部节点。
时间线
从浏览器加载页面开始到完全结束的过程,按顺序发生的总流程叫时间线。
- 加载文档
- 解析文档,构建 DOM 树,同时构建 CSS 树
- 遇到 link 标签,开新线程,异步加载 css 外部文件 => 构建 Css 树
- 没有设置异步加载的 script 会阻塞文档解析,需要等 js 脚本加载且执行完成后,继续解析文档
- 异步加载 script,异步加载并执行,不阻塞解析文档。(不能使用 document.write)
- 解析文档遇到 src,创建加载线程,异步加载图片资源。
- 文档解析完成
- defer script JS 脚本执行。(async script 是解析完就执行)
- 监听解析完成的事件(DOMContentLoaded 事件,文档解析后触发)
- async script 加载执行完毕,img 等资源加载完毕,window 对象中的 onload 触发。
- window.onload 在所有资源加载完毕才触发。
BOM
浏览器对象模型。
表示由浏览器(主机环境)提供的用于处理文档(document)之外的所有内容的其他对象。
包括的内容有: navigator,screen,location,frames,history,XMLHttpRequest 等.
函数 alert/confirm/prompt 也是 BOM 的一部分:它们与文档(document)没有直接关系,但它代表了与用户通信的纯浏览器方法。
BOM 和 DOM 的区别
DOM:
- 文档对象模型顶
- 级对象是 document
- 可以用来操作 html 页面的元素
- 标准化是 w3c 来制定
BOM:
- 浏览器对象模型
- 顶级对象是 window
- 用来和浏览器之间进行交互
- 是由各浏览器厂商在各自浏览器上定义,没有一个统一的标准
- 其实 DOM 对象就相当于是 BOM 儿子,因为 DOM 对象的操作都是由浏览器来执行的
作用
BOM 就是提供了一些访问窗口对象的一些方法,我们可以用它来移动窗口位置、改变窗口大小、打开新窗口、关闭窗口、弹出对话框、进行导航以及获取客户的一些信息如:浏览器品牌版本、屏幕分辨率….等等
常用方法
- prompt():弹窗输入
- alert():弹窗输出
- confirm:带确定取消的提示框,分别返回 true,false
- close:关闭当前浏览器窗口.
- open:打开一个新窗口
- setTimeout:延时器
- setInterval:定时器.
event
事件句柄,代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。简单说就是事件在发生时进行的一个操作。
onblur; 元素失去焦点。
onchange; 域的内容被改变。
onclick; 当用户点击某个对象时调用的事件句柄。
onfocus; 元素获得焦点。
onmousedown;鼠标按钮被按下。
onmousemove;鼠标被移动。
onmouseout;鼠标从某元素移开。
onmouseover;鼠标移到某元素之上。
History 对象
length; 查看浏览器的历史访问的网页的个数;
back(); 加载 history 列表中的前一个 url
forward();加载 history 列表中的下一个 url
go(); 加载 history 列表中的某个具体页面
go(0);相当于刷新页面
location 对象
封装当前打开窗口的 url
1 | console.log(location); |
Screen 对象
height;屏幕的像素高度
width;屏幕的像素宽度
availHeight;屏幕的像素高度减去系统部件高度之后的值(只读)
availWidth;屏幕的像素宽度减去系统部件宽度之后的值(只读)
availLeft;未被系统部件占用的最左侧的像素值(只读)[chrome 和 firefox 返回 0,IE 不支持]
availTop;未被系统部件占用的最上方的像素值(只读)[chrome 和 firefox 返回 0,IE 不支持]
Navigator
userAgent;包含浏览器的名称、内核、版本号等。
onLine;表示是否连接到了因特网。
坐标
offsetX,clientX,pageX,scrollX,getBoundleClientRect
offset
offsetParent 指对最近的一个有定位属性的元素。
offsetTop: 从元素上边框到 offsetParent 上边框的距离。
offsetWidth:返回元素的宽度,包括 padding,border。
offsetX:鼠标距离当前元素左边框的距离。
page
page 只有两个鼠标相关的属性
pageX: 鼠标距离文档顶部的距离,包括滚动滚去的。
pageY: 鼠标距离文档左边的距离。
client
clientTop: 元素的上边框高度。
clientLeft: 元素右边框宽度,如果有滚动条,包括滚动条。
clientWidth: 元素宽度,padding+content
clientX: 鼠标距离视口顶部高度
scroll
scrollTop: 上下滚动条滚动的距离
scrollLeft: 左右滚动条滚动的距离
scrollWidth: 元素实际宽度,content+padding+scrollLeft 最大值
scrollHeight: 元素实际高度,content+ padding+ scrollTop 最大值
scrollX: 文档在水平方向已滚动的像素值
getBoundingClientRect
获取页面中某个元素上下左右相对于视口的位置。
缺点:非实时。
查看计算样式 getComputedStyle
window.getComputedStyle(elem, null)[prop]
IE8:
elem.currentStyle
1 | window.getComputedStyle(div, null)["height"]; // "200px" |
坐标系区别
一、clientX、clientY
点击位置距离当前 body 可视区域的 x,y 坐标
二、pageX、pageY(IE9 以下不支持)
对于整个页面来说,包括了被卷去的 body 部分的长度
三、screenX、screenY
点击位置距离当前电脑屏幕的 x,y 坐标
四、offsetX、offsetY(包含边框,safari 不包含边框)
相对于带有定位的父盒子的 x,y 坐标
五、x、y
同 clientX,不建议使用。兼容性不好。
六、layerX、layerY
同 pageX,不建议使用。IE11 以下同 client。
滚动条距离
常规:window.pageXOffset/pageYOffset
IE9 以下:
document.body.scrollLeft/scrollTop
document.doucmentElement.scrollLeft/scrollTop
可视区域
常规:window.innerWidth/innerHeight
IE9/IE8:
标准:document.doucmentElement.clientWidth、clientHeight
怪异:document.body.clientWidth、clientHeight
浏览器兼容模式
怪异模式和标准模式
html 文件开头写了<!DOCTYPE html>
就是标准模式,否则是怪异模式,就是向后兼容模式。
DOM
文档对象模型。
getElementById() 返回带有指定 ID 的元素。
getElementsByTagName() 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。
getElementsByClassName() 返回包含带有指定类名的所有元素的节点列表。
appendChild() 把新的子节点添加到指定节点。
removeChild() 删除子节点。
replaceChild() 替换子节点。
insertBefore() 在指定的子节点前面插入新的子节点。
createAttribute() 创建属性节点。
createElement() 创建元素节点。
createTextNode() 创建文本节点。
getAttribute() 返回指定的属性值。
setAttribute() 把指定属性设置或修改为指定的值。
innerHTML - 节点(元素)的文本值
parentNode - 节点(元素)的父节点
childNodes - 节点(元素)的子节点
attributes - 节点(元素)的属性节点
Element
这是一个基类。所有 Document 对象下的对象都继承自它。
getElementsByClassName()
方法返回一个即时更新的(live)HTMLCollection,这是个集合,也即是类数组对象。包含了所有拥有指定 class 的子元素
getElementByTagName()
返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)
getElementById()
返回一个匹配特定 ID 的元素。
getAttributeNode()
返回指定元素的指定属性节点
querySelector()
方法返回文档中与指定选择器或选择器组匹配的第一个 Element 对象。如果找不到匹配项,则返回 null。
querySelectorAll(selector)
选择器是一个 css 选择器。
返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。
返回的对象是 NodeList 。这也是个类数组对象。
createElement
创建元素。
1 | document.createElement("div"); |
appenChild
属于 Node.prototype。
增加子节点。如果操作已有节点,就是剪切节点。
insertBefore(a,b)
插入节点。
1 | c.insertBefore(a, b); // 在父节点c内,子节点b之前插入子节点a |
removeChild
删除子节点。
节点
节点包括 nodeType,nodeName,nodeValue.
nodeType
属性返回节点的类型,nodeType 只读.
元素类型 | NodeType |
---|---|
元素 | 1 |
属性 | 2 |
文本 | 3 |
注释 | 8 |
文档 | 9 |
每个节点都有一个 childNodes 属性,里边是一个 NodeList 对象(类数组),它是基于 DOM 结构动态执行查询的结果,因此 DOM 结构的变化能够自动反映在 NodeList 对象中。
同样每个节点还有一个 children 的属性,它与 childNodes 的区别就是,childNodes 存储的是 NodeList 对象,而 children 返回的是 HTMLCollection 对象。NodeList 存储的不只是元素节点,也有文本节点、注释节点等等,而 HTMLCollection 存储的只有元素节点。
1 | <div id="test">hello <span>word</span></div> |
nodeName
属性名并不只是存在与属性节点,任何节点都有属性名。nodeName 属性规定节点的名称。他们的属性名如下:
元素节点的 nodeName 与标签名相同
属性节点的 nodeName 与属性名相同
文本节点的 nodeName 始终是 #text
文档节点的 nodeName 始终是 #document
属性值 nodeValue
nodeValue 属性规定节点的值,也就是每个属性名,就有会他对应的属性值。
元素节点的 nodeValue 是 undefined 或 null
文本节点的 nodeValue 是文本本身
属性节点的 nodeValue 是属性值
属性节点 AttributeNode
什么是属性?我们元素中的内置对象,就是属性节点。比如 style,class, id,都称为元素的属性节点。
Element.getAttributeNames();//获取属性名
Element.getAttribute(“class”);//获取 class 的属性值
Element.attributes.item(0);//获取元素第一个属性键值对
Element.hasAttribute(“class”);//判断是否有 class 属性
Element.setAttribute(“class”, “active”);//设置 class 属性
比如我们需要重置元素 class 的值,我们就可以通过 setAttribute 重新给 class 赋值。
当然还有一个内置 class 对象可以操作:Element.classList.add(“active”, “show”)
介绍到这里,你能想象一下 vue 的的 v-if 或者 v-on 是怎么实现的吗?简单的来说,他就是把他当成一个元素属性,获取到对应的标识,再去解析。
innerHTML
可以对 DOM 节点取值或者赋值。
取值会返回 html 字符串。
赋值追加可以执行div.innerHTML += '123'
会被解析。
innerText
返回字符实体。带转义字符的。
如果赋值,就会替换原内容
textContext
返回源字符。不带转义字符,页面中显示的。
不会被解析。
区别
textContext 获取所有元素的内容。
innerText 只获取给人看的内容。
innerText 会受到 CSS 的影响,reflow。
innerHTML 容易有安全性问题。
自定义属性 data-*
1 | <div data-name="abc" data-age="18"> |
可以通过 dataset 访问。div.dataset.name = 'abc'
或者getAttribute
:div.getAttribute('data-name')
类数组对象
所谓类数组对象,就是指可以通过索引属性访问元素并且拥有 length 属性的对象。
常见的类数组对象:arguments
,nodeList
,typedArray
。
区别
事件机制
事件触发过程是怎样的? 什么是事件代理?
事件触发(事件流 )
三个阶段: 捕获阶段 => 事件(目标)阶段 => 冒泡阶段
事件源
事件作用在哪个元素上,那个元素就是事件源。
事件对象
就是事件函数返回的 e
target 和 currentTarget
target: 事件源对象。指当前真正点击的元素
currentTarget: 事件绑定的元素.
事件绑定
有三种方法.
1 | // 1. 在DOM中直接绑定 |
事件监听和事件绑定
事件监听和事件绑定的最大区别就是事件监听可以给一个事件监听多个函数操作,而事件绑定只有一次.
1 | // 可以监听多个,不会被覆盖 |
事件冒泡和事件捕获
顺序:先执行捕获,后执行冒泡。
区别:捕获是父级捕获子级的事件,冒泡是子级向父级冒泡。
1 | box.addEventListener( |
没有冒泡和捕获事件的: focus
, blur
, scroll
,change
,submit
,reset
,select
.
阻止事件冒泡和捕获
stopPropagation 可以阻止事件传播,但是不能阻止事件发生.
例如对链接的点击仍会处理.
1 | event.stopPropagation(); // 支持IE9+ |
取消默认事件
阻止事件触发后的默认动作的发生.
1 | // 取消右键菜单 |
事件代理(委托)
如果我们有许多以类似方式处理的元素,那么就不必为每个元素分配一个处理程序 —— 而是将单个处理程序放在它们的共同祖先上
在处理程序中,我们获取 event.target 以查看事件实际发生的位置并进行处理。
在 ul 列表绑定事件,就不用在每个 li 元素上单独绑定了,因为会冒泡到父元素上,通过 event.target 拿到.
1 | <body> |
事件委托的特点
1、提高 JS 性能
2、动态的添加 DOM 元素
a 标签事件
1 | <a href="javascript:void(0)"></a> |
上面执行void(0)
,就是 return 0。也就不会跳转。
1 | // 锚点,点击跳转到顶部 |
HTML5
input 的自动聚焦(autofocus)
拖动 drag
1 | <div class="dropzone"></div> |
dataTransfer:拖拽目标的信息。
地理位置
1 | function success(e) { |
设备信息
devicemotion
加速度:devicemotion.acceleration.x
设备方向:deviceorientation.alpha,beta,gamma
文件信息
1 | <input type="file" id="flieInput" /> |
图片预览
Web Worker
介绍
运行在主线程之外的脚本,
分为专用线程和共享线程
用途
由于 js 是单线程,遇到复杂运算,就会影响渲染进程,造成页面卡顿.
就可以把复杂运算放到 webworker 中,等到计算结束,将结果返回给主线程.
限制
- 同源限制: 分配给 webworker 的脚本文件必须与主线程同源
- 文件限制: workers 线程是运行在后台的,它所加载的文件必须是线上的,不能是本地的
- DOM 限制: workers 线程不能直接操作 DOM,如果要处理 DOM 对象,必须是将结果返回给主线程,由主线程操作
- 脚本限制: 不能使用 alert 和 confirm 方法,可以使用 ajax 请求
- 通信限制: workers 线程和主线程不在同一个上下文,无法使用 window 对象,不能直接通信,只能通过消息.
用法
在主线程中创建 worker,先判断是否支持 worker
1 | if (window.worker) { |
专用线程由Worker()
方法创建,接收两个参数,第一个参数必填,是脚本的位置.第二个参数是可选的配置对象.
可以指定type
,credentials
,name
三个属性.
1 | let worker = new Worker("worker.js"); |
注意: 因为 webworker 有同源策略,本地调试时也需要启动本地服务器访问.
数据传递
webworker 和主线程都是通过postMessage()
方法发送消息,通过 onmessage 事件接收消息.
- 此过程中的数据不是共享而是复制的.
Error
和Function
对象不能被结构化克隆算法复制,被报错- PostMessage 一次只能发一个对象.多个参数可以包装成数组或对象传递
1 | // 主线程 |
在子线程中,self 和 this 都可以表示子线程的全局对象.
下面 4 种方法通用.
1 | // 写法 1 |
共享线程
主线程通过 MessagePort 访问专用线程和共享线程。专用线程的 port 会在线程创建时自动设置,并且不会暴露出来。与专用线程不同的是,共享线程在传递消息之前,端口必须处于打开状态。
start() 方法是与 addEventListener 配套使用的。如果我们选择 onmessage 进行事件监听,那么将隐含调用 start() 方法。
在传递消息时,postMessage() 方法和 onmessage 事件必须通过端口对象调用。另外,在 Worker 线程中,需要使用 onconnect 事件监听端口的变化,并使用端口的消息处理函数进行响应。
1 | // 主线程 |
关闭 worker
可以在主线程中使用 terminate() 方法或在 Worker 线程中使用 close() 方法关闭 worker。这两种方法是等效的,但比较推荐的用法是使用 close(),防止意外关闭正在运行的 Worker 线程。Worker 线程一旦关闭 Worker 后 Worker 将不再响应。
1 | // 主线程 |
错误处理
1 | // 主线程 |
上下文
Worker 工作在一个 WorkerGlobalDataScope 的上下文中。每一个 WorkerGlobalDataScope 对象都有不同的 event loop。这个 event loop 没有关联浏览器上下文(browsing context),它的任务队列也只有事件(events)、回调(callbacks)和联网的活动(networking activity)。
每一个 WorkerGlobalDataScope 都有一个 closing 标志,当这个标志设为 true 时,任务队列将丢弃之后试图加入任务队列的任务,队列中已经存在的任务不受影响(除非另有指定)。同时,定时器将停止工作,所有挂起(pending)的后台任务将会被删除。