最全的(前端知识)汇总,以便自身复习
上一篇内容:HTML/CSS2-CSS3
由于篇幅过于长,知识体系过于庞杂,请谨慎学习, 转述的我都有放置链接(难理解的也放了demo测试或者图)
技术规范(基础了解,大佬尽可跳过)
JS/ES5>>>>>ES6
JS基础知识(三部分:ECMAScript/DOM/BOM)
BOM
tips: browser object model(即浏览器对象模型,核心是window)
BOM是一系列在浏览器环境中使用对象的统称,这些对象提供了访问浏览器的功能。
- window对象有双重角色
通俗讲就是JavaScript访问浏览器的一个接口 ECMAScript规定的Global对象(全局对象)
介绍:我们定义的全局变量和全局函数都是window对象的属性和方法
镇山宝图(window对象思维导图)
查阅资料
- navigator对象
查阅资料 思否 JavaScript BOM对象-Navigator浏览器对象
- screen对象
参考JS高级程序设计-第3版,挺好的书(有需要找我要电子书)
- history对象
深入学习History对象管理浏览器会话历史 BOM之history对象 history对象 前端离开页面事件总结
- location对象
- document对象
navigator对象
navigator对象:包含大量有关Web浏览器的信息,在检测浏览器及操作系统上非常有用(window.navigator或者navigator) 每个浏览器中的 navigator 对象也都有一套自己的属性
基本大概有需要的
//以下也可以通过window.navigator获取到
navigator.appCodeName //浏览器代码名的字符串表示
navigator.appName //官方浏览器名的字符串表示
navigator.appVersion //浏览器版本信息的字符串表示
navigator.cookieEnabled //如果启用cookie返回true,否则返回false
navigator.javaEnabled //如果启用java返回true,否则返回false
navigator.platform //浏览器所在计算机平台的字符串表示
navigator.plugins //安装在浏览器中的插件数组
navigator.taintEnabled //如果启用了数据污点返回true,否则返回false
navigator.userAgent //用户代理头的字符串表示
Navigator.onLine //返回一个布尔值,表示用户当前在线还是离线(浏览器断线)
- js代码来确认当前浏览器的种类以及版本
var UserAgent =window.navigator.userAgent.toLowerCase();
function getBrowserType(UserAgent){
var result={
isIE6: /msie 6.0/.test(UserAgent), // IE6
isIE7: /msie 7.0/.test(UserAgent), // IE7
isIE8: /msie 8.0/.test(UserAgent), // IE8
isIE9: /msie 9.0/.test(UserAgent), // IE9
isIE10: /msie 10.0/.test(UserAgent), // IE10
isIE11: /msie 11.0/.test(UserAgent), // IE11
isLB: /lbbrowser/.test(UserAgent), // 猎豹浏览器
isUc: /ucweb/.test(UserAgent), // UC浏览器
is360: /360se/.test(UserAgent), // 360浏览器
isBaidu: /bidubrowser/.test(UserAgent), // 百度浏览
isSougou: /metasr/.test(UserAgent), // 搜狗浏览器
isChrome: /chrome/.test(UserAgent),
//Chrome浏览器
isFirefox: /firefox/.test(UserAgent), // 火狐浏览器
isOpera: /opera/.test(UserAgent), // Opera浏览器
isSafiri: /safari/.test(UserAgent) && !/chrome/.test
(UserAgent), // safire浏览器
isQQ: /qqbrowser/.test(UserAgent)//qq浏览器
};
return result;
}
console.log(getBrowserType(UserAgent));
screen 对象
只用来表明客户端的能力,包括浏览器窗口外部的显示器的信息,如像素高度和宽度等。每个浏览器中的screen对象都包含着各不相同的属性
-
各大浏览器实现的属性
ps:提及兼容性高的
| 属性 | 说明 | IE | FireFox | Safari/Chrome | Opera |
|---|---|---|---|---|---|
| availHeight | 返回显示屏幕的高度 (除 Windows 任务栏之外) | ✔ | ✔ | ✔ | ✔ |
| availWidth | 返回显示屏幕的宽度 (除 Windows 任务栏之外) | ✔ | ✔ | ✔ | ✔ |
| width | 返回显示器屏幕的宽度 | ✔ | ✔ | ✔ | ✔ |
| height | 返回显示屏幕的高度 | ✔ | ✔ | ✔ | ✔ |
| colorDepth | 返回目标设备或缓冲器上调色板的比特深度(多数系统32位) | ✔ | ✔ | ✔ | ✔ |
| pixelDepth | 返回显示屏幕的颜色分辨率(比特每像素) | × | ✔ | ✔ | ✔ |
| availLeft | 返回未被系统部件占用的最左侧的像素 | × | ✔ | ✔ | × |
| availTop | 返回未被系统部件占用的最上方的像素 | × | ✔ | ✔ | × |
history对象
History对象允许我们操作浏览器会话历史,即加载当前页面的标签页窗口或frame窗口的访问历史
-
属性(window.history获取)
-
history.length:包括当前页面在内的会话历史中的记录数量
-
history.state:属性用来保存记录对象(返回当前页面的state对象,上述的state对象)【区别不同会话历史纪录】
-
-
导航方法(window.history获取)
-
history.back():返回会话历史记录中的上一个页面
-
history.forward():进入会话历史记录中的下一个页面
-
history.go():加载会话历史记录中的某一个页面,该页面与当前页面在会话历史中的相对位置定位,-1代表当前页面的上一个记录,1代表当前页面的下一个页面
-
-
增改记录
-
方法
-
history.pushState():在浏览历史中添加【向浏览器历史添加了一个状态】
-
state —— 状态对象是一个由pushState()方法创建的、与历史纪录相关的javascript对象,当用户定向到一个新的状态时,会触发popstate事件
-
title —— 新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null
-
url —— 必须与当前页面URL同源
语法: history.pushState(state, title, url); var stateObj = { foo: "bar" }; history.pushState(stateObj, "page 2", "bar.html");
-
-
history.replaceState():在浏览历史中修改记录(更新当前会话历史记录,如更新当前会话记录的状态对象或URL。)
-
-
-
事件
-
popstate:监听history对象的变化【每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件】
仅仅调用pushState方法或replaceState方法,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用javascript调用back()、forward()、go()方法时才会触发
-
往返缓存概念(浏览器有一个特性)
-
可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度
-
这个缓存中不仅保存着页面数据,还保存了DOM和javascript的状态
-
如果页面位于bfcache中,那么再次打开该页面时就不会触发load事件
-
进入页面与离开页面事件 pageshow与 pagehide=》(无法执行alert操作,window 对象 和 dom 对象都已经被销毁了)
-
pageshow:页面加载时触发,包括第一次加载和从缓存加载两种情况 ps:第一次加载时,它的触发顺序排在load事件后面
window.onpageshow = function(e){ e = e || event; console.log(e.persisted,进入加载事件); } -
pagehide:该事件会在浏览器卸载页面的时候触发,而且是在unload事件之前触发
window.onpagehide = function(e){ e = e || event; console.log(e.persisted); }
-
前端离开页面事件总结(出现提示框有点会有点问题,因为alert是阻塞)
-
Onblur :监控(窗口 页面 组件)失去焦点事件
-
Onfocus :监控(窗口 页面 组件)得到焦点事件
-
onbeforeunload:在即将离开当前页面(刷新或关闭)时执行 JavaScript
window.onbeforeunload=function(e){   var e = window.event||e;   e.returnValue=("确定离开当前页面吗?"); }
location对象
- 属性
以http://www.baidu.com:8080/index.php?name=kang&when=2011#first为例
| 属性 | 描述 | 值 |
|---|---|---|
| protocol | 设置或返回当前 URL 的协议。 | http: |
| hostname | 设置或返回当前 URL 的主机名。 | www.baidu.com |
| host | 设置或返回主机名和当前 URL 的端口号 | www.baidu.com:8080 |
| port | 返回当前 URL 的端口号 | 8080 |
| pathname | 设置或返回当前 URL 的路径部分 | /index.php |
| search | 设置或返回从问号 (?) 开始的 URL(查询部分) | ?name=kang&when=2011 |
| hash | 设置或返回从井号 (#) 开始的 URL(锚) | #first |
| href | 设置或返回完整的 URL | 全链接 |
window.location和document.location互相等价。 location 的8个属性都是可读写,但是只有href与hash的写才有意义
-
方法
-
assign() :加载新的文档
-
导航到一个新页面的方法
window.location.assign("http://www.mozilla.org"); // or window.location = "http://www.mozilla.org";
-
-
reload() :重新加载当前文档
如果文档已改变,reload() 会再次下载该文档。如果文档未改变,则该方法将从缓存中装载文档。这与用户单击浏览器的刷新按钮的效果是完全一样的。如果把该方法的参数设置为 true,那么无论文档的最后修改日期是什么,它都会绕过缓存,从服务器上重新下载该文档
window.location.reload(true);- replace() :用新的文档替换当前文档
不会在 History 对象中生成一个新的记录。当使用该方法时,新的 URL 将覆盖 History 对象中的当前记录。
window.location.replace('http://example.com/'); -
document 对象
兼容问题
-
document.documentElement.body || document.body 区别 document.body 和 document.documentElement 的区别
- document.documentElement:返回html dom中的root 节点 即html
- document.body:返回html dom中的body节点 即body
-
获取 scrollTop 方面的差异
兼容性解决方案:var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
DTD概念普及 W3C HTML中 !DOCTYPE 的解释与作用
-
Document Type Definition,中文翻译为:文档类型定义【总体上应该分为三类: HTML5,HTML4.0,XHTML。】
-
早期的版本基于SGML,所以需要套用SGML的解析规则。DTD的作用在于定义SGML文档的文档类型以便于浏览器解析。
-
随着技术的进步,现在HTML5 不基于 SGML,所以也就不需要引用 DTD了
牵扯处一个问题什么是!DOCTYPE,以及作用(面试题)
-
DOCTYPE是document type(文档类型)的简写,在Web设计中用来说明你用的XHTML或者HTML是什么版本
-
没有<!DOCTYPE>声明,那么不同的浏览器将会以自己不同的怪异的模式去解析渲染页面,这样页面在不同的浏览器上呈现出来的效果也就不一样,人们把这称之为“怪异模式”
-
如果声明了,将会开启“严格模式”,又有人称之为“标准模式”,浏览器将已w3c标准来解析渲染页面
-
document获取到当前页面很多数据信息【属性】
-
所有图片images/表单forms/链接links/锚点anchors的集合
document.images/document.forms/document.links/document.anchors -
当前页的cookie/域名domain/获取载入当前文档的文档的url/标题title/当前url
document.cookie/document.domain/document.referrer/document.title/document.URL
-
-
方法
- document.write() :并将原有内容替换为一些不同的HTML片段【在页面加载后调用,会发生自动的 document.open()】
不要和 window.open() 方法混淆。document.open 可用于重写当前的文档内容或者追加内容, 而 window.open 是提供了打开一个新的窗口的方法,当前的网页文档内容会被保留
- document.open():文档中的所有节点会被清除
窗口控制
window.scrollBy(x,y)/window.scrollTo(x,y)挺有用的,移动文档
MoveTo和MoveBy的区别(可引申为To和By的区别)
-
By 和 To 的区别(move/resize/scroll)--窗口移动变化(浏览器禁止)/窗口大小变化(浏览器可能会禁止掉)/内容滚动
-
By 算的是相对于节点对象的当前位置
-
To 算的是绝对位置,不考虑当前节点对象在哪
-
moveBy()/moveTo()----->在chrome,IE浏览器上运行,都不显示效果
原因:为了保护计算机的安全性
定时器
- setInterval : 周期性的调用一个函数或者执行一段代码
- setTimeout : 指定延迟一定的时间后调用一个函数或者执行某一个动作
- clearInterval :清除setInterval设置的动作
- clearTimeout :清除setTimeout设置的动作
----语法不多赘述
- 重点
javascript都是以单线程的方式运行于浏览器的javascript引擎中的 settimeout和setinterval的作用只是把你要执行的代码在你设定的一个时间点插入js引擎维护的一个代码队列中 插入代码队列并不意味着你的代码就会立马执行的,理解这一点很重要 settimeout和setinterval还有点不一样
扯出最最重要的知识点:Event Loop(面试题) 通杀 Event Loop 面试题 / Js 的事件循环(Event Loop)机制以及实例讲解
js是单线程的脚本语言,在同一时间,只能做同一件事,为了协调事件、用户交互、脚本、UI渲染和网络处理等行为,防止主线程阻塞,Event Loop方案应运而生
-
知识点的普及
-
执行栈
当执行某个函数、用户点击一次鼠标,Ajax完成,一个图片加载完成等事件发生时,只要指定过回调函数,这些事件发生时就会进入任务队列中,等待主线程读取,遵循先进先出原则
-
主线程
- 主线程跟执行栈是不同概念,主线程规定现在执行执行栈中的哪个事件
- 主线程循环:即主线程会不停的从执行栈中读取事件,会执行完所有栈中的同步代码
-
任务队列(Task Queue)
- 遇到一个异步事件后,并不会一直等待异步事件返回结果,而是会将这个事件挂在与执行栈不同的队列中--》 任务队列
-
顺序讲解: 当主线程将执行栈中所有的代码执行完之后,主线程将会去查看任务队列是否有任务。如果有,那么主线程会依次执行那些任务队列中的回调函数。
-
宏任务与微任务(很重要)
-
异步任务分为 宏任务(macrotask) 与 微任务 (microtask),不同的API注册的任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。
-
宏任务(以下都是) script(整体代码)、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境)、UI rendering (浏览器独有)
-
微任务(以下都是) Promise.then()、 MutaionObserver、process.nextTick(Node.js环境)、Object.observe
-
-
Event Loop(事件循环) 【所有任务看成两个队列:执行队列与事件队列】
--->先执行script --->遇到宏任务(放入事件列表),遇到微任务(先依次压入执行栈),等script里的同步任务完成 --->再执行执行栈里面的微任务 --->最后执行事件列表里的宏任务
-
注意:new Promise() 构造函数里面是同步代码,而非微任务
promise.then 虽然和 process.nextTick 一样,都将回调函数注册到微任务,但优先级不一样。process.nextTick 的微任务队列 总是优先于 promise 的 微任务队列 执行
在 I/O Callbacks 中注册的 setTimeout 和 setImmediate,永远都是 setImmediate 先执行。
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
console.log(4);
1. 遇到setTimeout(宏任务)压到事件列表
2. 进入Promise,遇到console.log(2),打印2
3. 遇到resolve(3),回调函数【Promise.then是微任务】压入执行栈
3. 遇到console.log(4),执行4-------》执行完script
4. 执行微任务resolve(3),转到then,打印3
4. 最后执行事件列表,执行宏任务setTimeout,打印1
--》 2 4 3 1
-
巩固提高
console.time("start") setTimeout(function () { console.log(2); }, 10); setImmediate(function () { console.log(1); }); new Promise(function (resolve) { console.log(3); resolve(); console.log(4); }).then(function () { console.log(5); console.timeEnd("start") }); console.log(6); process.nextTick(function () { console.log(7); }); console.log(8); // requestAnimationFrame(() => console.log(9))。
讲完了这个执行顺序(贼重要)问题,回到计时器
关于setTimeout()你所不知道的地方,详解setTimeout()
- setTimeout和setInterval的运行机制
将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。这意味着, setTimeout和setInterval指定的代码,必须等到本次执行的所有代码都执行完,才会执行
由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeout和setInterval指定的任务,可能会不一定按照预定时间执行
- 异步函数的类型:在Javascript环境中提供的异步函数分为2大类:I/O函数和计时函数 Javascript异步编程之setTimeout与setInterval / JavaScript运行机制:事件驱动编程详解
-
setTimeout来代替setInterval
timer=setTimeout(fn,1000) function fn(){ console.log("循环") //argement.callee代表代表上下文正在执行的函数--》这些都会在后面一一细说 setTimeout(arguments.callee,1000) } -
setTimeout(fn,0)来置换顺序
-
setTimeout(fn,time),在执行过程中time是能改变的(调整快慢)
-
setTimeout循序回调(传参)
setTimeout(function(a,b){ console.log(a+b); },1000,1,1);
注意点:setTimeout()中回调函数中的this(会指向全局,利用apply/call解决) 你所不知道的setTimeout
为什么不使用 setInterval-->跳帧---》解决方案,比如容易内存泄漏 深度解密setTimeout和setInterval——为setInterval正名 / 从setTimeout-setInterval看JS线程(setInterval缺点) / 阮一峰的线程与进程
对话框
-
alert :显示警告框的信息; 无返回值
-
confirm:参数就只有一个.显示提示框的信息. 按确定,返回true; 按取消返回false.
var truthBeTold = window.confirm("单击“确定”继续。单击“取消”停止。"); if (truthBeTold) { window.alert("欢迎访问我们的 Web 页!"); } else { window.alert("再见啦!"); } -
prompt:参数,有两个, 第一个参数,显示提示输入框的信息. 第二个参数,用于显示输入框的默认值. 返回,用户输入的值.
var data = window.prompt("输入验证码", "") if( data == 'ss' ) { alert("ok"); } else { alert("false"); }
(重要)浏览器窗口属性
JavaScript中的各种宽高属性 / JS 相关宽高理解,有图表可以查阅
-
了解跨浏览器确定窗口大小是很不容易的:
-
window.innerHeight/innerWidth:第一种方式获取的是可视区的宽高,包括了滚动条的宽度
ps:IE 8 及更早 IE版本不支持这两个属性。
-
document.body.clientHeight/clientWidth: 第二种方式获取的是可视区的宽高,不包括滚动条的宽度以及边框border【 获取body宽度】
兼容性写法:document.documentElement.clientHeight/clientWidth(获取整个页面)
document.documentElement对象的clientWidth..... / 如何使用 JavaScript 获取页面、窗口的高度?
-
// 页面可视区的宽度
var oClientWidth = document.documentElement.clientWidth || document.body.clientWidth || 0;
// 页面可视区的高度
var oClientHeight = document.documentElement.clientHeight || document.body.clientHeight || 0;
-
document.body.offsetWidth/offsetHeight :是可视区的宽高(即元素占据的空间,包括填充和边框,不包括滚动条)【 获取body宽高度】
兼容性写法:document.documentElement.offsetWidth/offsetHeight(获取整个页面)
// scrollWidth 表示整个网页正文的宽度
var scrollWidth = document.documentElement.offsetWidth || document.body.offsetHeight;
// scrollHeight 表示整个网页正文的高度
var scrollHeight = document.documentElement.offsetWidth || document.body.offsetHeight;
-
document.body.scrollWidth/scrollHeight: 网页正文全文宽高【 获取body正文全文宽高】
兼容性写法:document.documentElement.scrollWidth/scrollHeight(获取整个页面正文全文宽高)
// scrollWidth 表示整个网页正文的宽度
var scrollWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
// scrollHeight 表示整个网页正文的高度
var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
-
页面正文卷起长度
- pageYOffset/pageXOffset ------ IE 8 及 更早 IE 版本不支持该属性,但可以使用 document.documentElement.scrollLeft 和 document.documentElement.scrollTop 属性
解决兼容:scrollTop/scrollLeft
// scrollTop 网页被卷起的高度
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
// scrollLeft 网页左边被卷起的宽度
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
-
window 相关宽高
-
Event对象的5种坐标
-
clientX和clientY,相对于浏览器(可是区左上角0,0)的坐标
-
screenX和screenY,相对于设备屏幕左上角(0,0)的坐标
-
offsetX和offsetY,相对于事件源左上角(0,0)的坐标
-
pageX和pageY,相对于整个网页左上角(0,0)的坐标
-
X和Y,本来是IE属性,相对于用CSS动态定位的最内层包容元素
DOM
关于DOM级别的一些问题 DOM事件全整理之从DOM事件级别,DOM事件流到事件委托
文档对象模型 (DOM) 是HTML和XML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构,样式和内容。
DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。
简单来讲,DOM就是一组API(接口)。它将一份结构化文档看做一棵树,这棵树由各种各样的节点构成,也即节点树
精辟:DOM标准的目标是让“任何一种程序设计语言”能操控使用“任何一种标记语言”编写出的“任何一份文档”。“操控”具体含义为能通过DOM提供的API对文档的内容、结构、样式进行访问和修改
获取节点
-
方法
-
getElementById():根据元素的id来获取该元素对象,通过该方法获取的元素对象是唯一的,可以直接对其进行操作
-
getElementsByClassName():根据元素的class类名来获取该元素对象,通过该方法获取的元素对象是一个伪数组,需要通过伪数组的方式对其进行访问
-
getElementsByTagName():根据元素的标签名来获取该元素对象,通过该方法获取的元素对象是一个伪数组,需要通过伪数组的方式对其进行访问
-
是 HTML5 新增加的一个 js 函数,对于一些老浏览器例如 IE9 以下的浏览器是不支持这个函数的,所以在那些需要处理兼容性问题的
getElementsByClassName()方法不兼容ie低版本解决方案
if(!document.getElementsByClassName)//判断浏览器是否支持这个方法
{
document.getElementsByClassName=function(cname){
var selected=new Array();
var alltag=document.getElementsByTagName("*");//获取所有标签
for(var i=0;i<alltag.length;i++)
{
var t=alltag[i];
alert(t.className);
if(t.className==cname) //比较标签的class与所要查找的class是否相同
{
selected.push(t); //将相同的存入数组
}
}
return selected;
}
}
querySelector与getElement...差异以及区别 querySelector和getElementById性能分析与使用选择 / 为何getElementsByTagName()比querySelectorAll()快100倍
-
document.querySelector():返回文档中匹配指定的CSS选择器的第一元素
document.querySelector("#demo");
-
document.querySelectorAll():是 HTML5中引入的新方法,返回文档中匹配的CSS选择器的所有元素节点列表
var x = document.querySelectorAll(".example");
节点指针(节点下的指定节点)
认识DOM文档的遍历指针以及如何获取根节点和body节点 / 《JavaScript高级程序设计》学习笔记(四)
知识点普及:Node类型
DOM1级定义了一个Node接口,该接口由DOM中的所有节点类型实现。这个Node接口在JavaScript中是作为Node类型实现。除了IE之外,在其他浏览器中都可以访问到这个类型。JavaScript中所有节点类型都继承自Node类型,因此所有节点类型都共享着相同的基本属性和方法
在DOM结构树中,每一个独立的DOM节点都定义了一系列的指针(节点属性),通过这些指针,我们可以遍历DOM结构中我们想找到的任何对象。如果指针指向的元素不存在,则对应指针的属性值为null
-
nodeName和nodeValue属性-------》【扩展到js原生事件委托机制(事件的捕获冒泡)】
- 知识点普及
用nodeName和nodeValue属性可以了解节点的具体信息,其值完全取决于节点的类型 nodeName中保存的始终都是元素的标签名,而nodeValue的值则始终为null
Javascript性能优化 - 事件委托 / JavaScript 事件委托详解 / 事件冒泡机制与委托机制 / JavaScript事件委托原理 / JavaScript事件代理和委托(Delegation)
-
事件委托机制
-
场景:
- 元素过多时的性能问题
ps:假设有一个多行多列的表格,我们想让用户单击每个单元格都能看到与其中内容相关的更多信息(比如,通过提示条)。为此,可以为每个单元格都绑定click事件.问题是,如果表格中要绑定单击事件的有10列500行,那么查找和遍历5000个单元格会导致脚本执行速度明显变慢,而保存5000个td元素和相应的事件处理程序也会占用大量内存
- 动态添加元素的无法直接绑定事件
ps:假设一个动态翻页的表格,每一次翻页都是重新插入的表格。通过给第一页添加事件,对后续通过js异步加载的页面没有作用
-
提示:使用事件委托时,如果注册到目标元素上的其他事件处理程序使用.stopPropagation()阻止了事件传播,那么事件委托就会失效。
-
事件触发过程:现代浏览器(指 IE6-IE8 除外的浏览器,包括 IE9+、FireFox、Safari、Chrome 和 Opera 等)事件流包含三个过程,分别是捕获阶段、目标阶段和冒泡阶段 JavaScript 浏览器事件解读
- 捕获阶段
当我们对 DOM 元素进行操作时,比如鼠标点击、悬浮等,就会有一个事件传输到这个 DOM 元素,这个事件从 Window 开始,依次经过 docuemnt、html、body,再不断经过子节点直到到达目标元素,从 Window 到达目标元素父节点的过程称为捕获阶段,注意此时还未到达目标节点。
- 目标阶段
捕获阶段结束时,事件到达了目标节点的父节点,最终到达目标节点,并在目标节点上触发了这个事件,这就是目标阶段。
- 冒泡阶段
当事件到达目标节点之后,就会沿着原路返回,这个过程有点类似水泡从水底浮出水面的过程,所以称这个过程为冒泡阶段。
addEventListener(eventName, handler, useCapture) 函数。第三个参数是 useCapture,代表是否在捕获阶段进行事件处理, 如果是 false, 则在冒泡阶段进行事件处理,如果是 true,在捕获阶段进行事件处理,默认是 false。这么设计的主要原因是当年微软和 netscape 之间的浏览器战争打得火热,netscape 主张捕获方式,微软主张冒泡方式,W3C 采用了折中的方式,即先捕获再冒泡。(true 时表捕获阶段,false时为冒泡阶段)
main.addEventListener("mousedown",function(e){ //e.target是鼠标经过的那个元素
var nodename = e.target.nodeName.toLocaleLowerCase(); //标签转小写
var classname=e.target.className; //获取class
if(e.target && nodename == "div" || nodename=="span") {
if(classname=="head"||classname=="msg"||classname=="foot"||classname=="tip"|| nodename=="span"){
if(classname=="tip"){
...........
}else if(nodename=="span"){
........
}else{
............
}
}
}
},true);
-
事件委托是事件冒泡的一个应用,但有时候我们并不希望事件冒泡
-
event.stopPropagation():阻止冒泡事件,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉这个方法的时候,如果点击一个链接,这个链接仍然会被打开;
ps:但IE不支持,IE中提供的是,cancelBubble属性,默认为false,当它设置为true时,就是阻止事件冒泡
-
event.preventDefault():阻止默认事件,调用此方法是,链接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;
-
return false:这个方法比较暴力,他会同时阻止事件冒泡也会阻止默认事件;写上此代码,链接不会被打开,事件也不会传递到上一层的父元素,可以理解为 return false 就等于同时调用了event.stopPropagation()和event.preventDefault()。
-
-
讲到冒泡=>其实可以扯到onmouseover/onmouseout 以及onmouseenter/onmouseleave
事件冒泡以及onmouseenter 和 onmouseover 的不同
-
冒泡 onmouseover/onmouseout是有冒泡的
onmouseenter/onmouseleave是没有冒泡的
-
mouseover
- event.target —— 是鼠标经过的那个元素。
- event.relatedTarget —— 是鼠标上一次经过的元素。
-
mouseout
- event.target —— 是鼠标离开的元素。
- event.relatedTarget —— 是当前指针位置下的(鼠标进入的)元素。
-
-
Node(节点)对应的5个指针
-
parentNode:返回指定节点的父节点
-
previousSibling:返回指定节点的上一个相邻节点
-
nextSibling:返回指定节点的下一个相邻节点
-
firstChild:返回指定元素的第一个子节点
-
lastChild:返回指定元素的最后一个子节点
-
childNodes
-
test.firstChild=test.childNodes[0] 始终相等
-
test.lastChild=test.[test.childNodes.length-1] 始终相等
-
在只有一个子节点的情况下,firstChild和lastChild指向同一个节点,如果没有子节点,firstChild和lastChild的值均为null。
-
-
操作节点
JavaScript常见原生DOM操作API总结 JavaScript操作DOM常用的API
-
创建节点
-
createElement:传入指定的一个标签名来创建一个元素
let elem = document.createElement("div"); elem.id = 'test'; elem.style = 'color: red'; elem.innerHTML = '我是新创建的节点'; document.body.appendChild(elem); -
createTextNode:创建一个文本节点
var node = document.createTextNode("我是文本节点"); document.body.appendChild(node); -
cloneNode:返回调用该方法的节点的一个副本
-
是否采用深度克隆,如果为true,则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身
-
要调用appendChild方法才能添加到文档树中
-
如果复制的元素有id,则其副本同样会包含该id,由于id具有唯一性,所以在复制节点后必须要修改其id
-
调用接收的是否深度克隆参数最好传入,如果不传入该参数,不同浏览器对其默认值的处理可能不同
-
需要克隆的元素,事件都启用,最好使用事件委托机制
var parent = document.getElementById("parent"); document.getElementById("btnCopy").onclick = function(){ var parent2 = parent.cloneNode(true); parent2.id = "parent2"; document.body.appendChild(parent2); }
-
-
createDocumentFragment:创建一个 DocumentFragment ,也就是文档碎片,它表示一种轻量级的文档,主要是用来存储临时节点,大量操作DOM时用它可以大大提升性能。
(function() { var start = Date.now(); var str = '', li; var ul = document.getElementById('ul'); var fragment = document.createDocumentFragment(); for(var i=0; i<1000; i++) { li = document.createElement('li'); li.textContent = '第'+(i+1)+'个子节点'; fragment.appendChild(li); } ul.appendChild(fragment); })();
-
-
插入节点
-
appendChild:前面已经用到多次,就是将指定的节点添加到调用该方法的节点的子元素的末尾。
-
insertBefore(newElement, referenceElement):用来添加一个节点到一个参照节点(referenceElement)之前
<div id="parent"> 父节点 <div id="child"> 子元素 </div> </div> <input type="button" id="insertNode" value="插入节点" /> var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.getElementById("insertNode").onclick = function(){ var newNode = document.createElement("div"); newNode.textContent = "新节点" parent.insertBefore(newNode,child); }
-
-
替换节点 :
-
parent.replaceChild(newChild,oldChild)用于使用一个节点替换另一个节点【oldChild是被替换的节点】
<body> <div id="parent"> 父节点 <div id="child"> 子元素 </div> </div> <input type="button" id="insertNode" value="替换节点" /> </body> <script> var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.getElementById("insertNode").onclick = function(){ var newNode = document.createElement("div"); newNode.textContent = "新节点" parent.replaceChild(newNode,child) }
-
-
移除节点
- removeChild:parent.removeChild(node)
回忆jquery的节点操作 jQuery DOM 操作(基本操作、内部插入、外部插入、包裹操作) / jQuery DOM节点的创建、插入、删除
属性操作
-
element.setAttribute(name, value)
let div1 = document.getElementById("div1"); div1.setAttribute("align", "center"); -
element.getAttribute(attributeName)
let div1 = document.getElementById("div1"); let align = div1.getAttribute("align"); -
element.removeAttribute(attrName)
let div = document.getElementById("div1") div.removeAttribute("style");
文本操作
暂了解,基本不使用除了前四种
---》获取CSS衍生出一个问题getComputedStyle()与currentStyle()、style()方法
-
obj.style:这个方法只能JS只能获取写在html标签中的写在style属性中的值(style=”…”),而无法获取定义在<style type="text/css" 里面的属性
-
IE中使用的是obj.currentStyle方法,而FF是用的是getComputedStyle 方法
解决兼容问题
function getStyle(element, attr) {
if(element.currentStyle) {
return element.currentStyle[attr];
} else {
return getComputedStyle(element, false)[attr];
}
}
收集DOM操作的一些坑提及一些知识普及(建议观看内容)
JavaScript 操作 DOM 的那些坑 /// 建议看看在新窗口中打开页面?小心有坑! /// 前端性能优化之 DOM 篇 /// 前端性能优化之图片篇 /// 关于反爬虫(爬虫与反爬虫),看这一篇就够了 /// 浏览器特性与安全策略 /// 【干货】浅析浏览器安全 /// 如何应对网站反爬虫策略?如何高效地爬大量数据? /// 浅析最烦人的反爬虫手段 /// 反AdBlock屏蔽广告
事件(比较重要的)
-
IE8不兼容addEvntListener,只能使用attachEvent()和detachEventListener()方法。IE从IE9开始支持addEventListener和removeEventListener. addEventListener/attachEvent兼容IE浏览器与标准浏览器
-
鼠标滚轮事件:mousewheel:》当用户使用鼠标滚轮与页面进行交互、在垂直方向上滚动页面时(无论上下),就会触发mousewheels事件,这个事件可以在任何元素上触发,最终会冒泡到document或者window上。
-
对应的event对象还有wheelDelta属性,值是120/-120的倍数
document.addEventListener('mousewheel',function(event){ console.log(event.wheelDelta) })
-
-
键盘事件
-
键盘事件用来描述键盘行为,主要有keydown、keypress、keyup三个事件
-
按下enter键
function enterOperate(evt){ if(evt.keyCode == 13){ alert("按下enter键"); } } document.body.addEventListener("keydown", enterOperate) -
组合按键
function combinationKey(evt){ if( evt.shiftKey && evt.keyCode == 73){ alert(“触发了shift + i组合键”); } }
-
程序员发展到一定的水平后,瓶颈并不在技术水平上,而是在表达能力上
涉及到点击穿透的问题【web开发经典问题】(面试题)点击穿透
用户点击事件之后,浏览器会等待300ms后,如果没有(再次的点击行为)【双击】行为,再去执行click事件
浏览器等待约 300ms 的原因是,判断用户是否是双击(double tap)行为,双击过程中就不适合触发 click 事件了 为了减少这300ms的延迟,tap事件被很多框架(如zepto)封装,来减少这延迟问题, tap事件不是原生的,所以是封装的
fastclick库。github
节流防抖衍生知识链(面试点)
- 阐述问题
上面说到点击问题,其实可以再扯到节流与防抖 面试准备 - JS 防抖与节流
讲到节流防抖---拉扯到回流重绘的问题
最后说到整个浏览器从输入url过程:DNS解析>tcp三次握手>tcp四次挥手>浏览器的渲染页面
-
解决说明问题【倒序解决】
-
输入url之后发生了什么 参考浏览器输入 URL 后发生了什么?
-
DNS解析 :( Domain Name System)是“域名系统”的英文缩写,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。
-
tcp三次握手:
-
专业名词
- SYN 表示SYN报文(在建立TCP连接的时候使用)
- ACK 表示Acknowledgment Number字段有意义
- FIN 表示没有数据需要发送了(在关闭TCP连接的时候使用)
-
过程
-
第一次握手:状态【两端都处于CLOSED关闭状态】,客户端(Client)将SYN的数据包【SYN=1,随机产生一个值seq=x】发送给服务端(Server),客户端(Client)处于SYN-SENT,等到服务端确认
-
第二次握手:服务端(Server)接收到数据包后,得知客户端请求建立连接,服务端将SYN与ACK【SYN=1,ACK=1,ack = x + 1,随机产生一个值 seq = y】)的数据包发回给客户端(Client),服务端进入SYN-RCVD状态,操作系统为该 TCP 连接分配 TCP 缓存和变量
-
第三次握手:客户端(Client)收到确认后,检查原先自己的seq是否等于x+1,ACK是否为1,如果没问题,将ACK数据包【ack = y + 1】丢回服务端(server),Server 检查 ack 是否为 y + 1,ACK 是否为 1,如果正确则连接建立成功。状态【两端都处于established 状态】,完成握手,随后 Client 和 Server 就可以开始传输数据。
-
-
-
TCP四次挥手
看懂上面的然后这里简单说:-
第一次挥手:客户端发送FIN【FIN=1,seq=u】数据包,停止再发送数据,主动关闭 TCP 连接,进入 FIN-WAIT-1(终止等待1)状态
-
第二次挥手:服务端接收到FIN数据包报文后,就会发出确认报文段ACK数据包【ACK = 1,确认号 ack = u + 1,序号 seq = v】,Server 进入 CLOSE-WAIT(关闭等待)状态,此时的 TCP 处于半关闭状态,Client 到 Server 的连接释放。 ----------------------------------------------------------》数据发送完毕
-
第三次挥手:Server 已经没有要向 Client 发出的数据了,Server 发出连接释放报文段(数据包)FIN与ACK(FIN = 1,ACK = 1,序号 seq = w,确认号 ack = u + 1),Server 进入 LAST-ACK(最后确认)状态,等待 Client 的确认。
-
第四次挥手:Client 收到 Server 的连接释放报文段后,对此发出确认报文段(ACK = 1,seq = u + 1,ack = w + 1),Client 进入 TIME-WAIT(时间等待)状态。此时 TCP 未释放掉,需要经过时间等待计时器设置的时间 2MSL 后,Client 才进入 CLOSED 状态。
-
四次挥手的详述 Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了
-
-
浏览器的渲染:【干货】十分钟读懂浏览器渲染流程 /// 浏览器渲染页面过程与页面优化
-
-
回流与重绘:重点:浏览器的回流与重绘 (Reflow & Repaint) /// 你真的了解回流和重绘吗
-
-
回到最初的问题:节流与防抖(这是俩种解决方案:解决的问题是:频繁的点击事件)
-
节流:指定一段时间内只执行一次操作
节流函数体 function throttle(fn) { // 4、通过闭包保存一个标记canRun let canRun = true; return function() { // 5、在函数开头判断标志是否为 true,不为 true 则中断函数 //作用执行函数canRun为false,然后一段时间后才变成true if(!canRun) { return; } // 6、将 canRun 设置为 false,防止执行之前再被执行 canRun = false; // 7、定时器 setTimeout( () => { fn.call(this, arguments); //将不确定变量替换到函数中了 // 8、执行完事件(比如调用完接口)之后,重新将这个标志设置为 true canRun = true; }, 1000); }; // 3、需要节流的事件 function sayThrottle() { console.log("节流成功!"); }- 引用场景
懒加载要监听计算滚动条的位置,使用节流按一定时间的频率获取。 用户点击提交按钮,假设我们知道接口大致的返回时间的情况下,我们使用节流,只允许一定时间内点击一次。
-
防抖函数:俩次触发间隔超过指定时间间隔
window.onload = function() { // 1、获取这个按钮,并绑定事件 var myDebounce = document.getElementById("debounce"); myDebounce.addEventListener("click", debounce(sayDebounce)); } // 2、防抖功能函数,接受传参 function debounce(fn) { // 4、创建一个标记用来存放定时器的返回值 let timeout = null; return function() { // 5、每次当用户点击/输入的时候,把前一个定时器清除 clearTimeout(timeout); // 6、然后创建一个新的 setTimeout, // 这样就能保证点击按钮后的 interval 间隔内 // 如果用户还点击了的话,就不会执行 fn 函数 timeout = setTimeout(() => { fn.call(this, arguments); }, 1000); }; } // 3、需要进行防抖的事件处理 function sayDebounce() { // ... 有些需要防抖的工作,在这里执行 console.log("防抖成功!"); }
-
输入URL发生了什么=参考文章
DNS解析:DNS解析的过程是什么,求详细的? /// DNS解析过程详解
TCP三次握手以及TCP四次挥手 TCP三次握手与四次挥手 /// 注意!是TCP不是HTTP的3次握手与4次挥手(#...#)
浏览器的渲染:【干货】十分钟读懂浏览器渲染流程