一.详解浏览器事件捕获与冒泡
1. 事件委托/事件代理
捕获阶段-->目标阶段-->冒泡阶段
window.addEventListener('click',function(e){
console.log('window捕获',e.target.nodeName,e.currentTarget.nodeName)
},true)
- 第三个参数为true,则监听捕获事件,不传或者传false,则监听冒泡事件
-
e.target
指的是点击的元素,e.currentTarget
指的是当前触发事件的元素
2. 阻止事件的传播
e.stopPropagation()
真正的作用是阻止事件的传播,既可以阻止事件的捕获也可以阻止事件的冒泡
面试题:
现在有一个页面,这个页面上有许多元素,div、p、button等
每个元素都有自己的click事件,都不相同
现在有一个新需求,一个用户进入这个页面的时候,会有一个状态:banned
,我们可以在全局上拿到这个状态:window.banned
,
为true
的话表示当前用户被封禁,用户点击页面的任何元素都不执行原有逻辑,而是alert
弹窗,提示你被封禁了;
为false
的话表示有操作权限。
请问:如何实现?
答:
方案一:在最外层的一个元素上绑定上一个捕获事件,即addEventListener
里的第三个参数为true
,如果window.banned
为true
则e.stopPropagation()
。
//const body = document.getElementsByTagName('body')
window.addEventListener('click',function(e){
if(window.banner){
e.stopPropagation()
alert('你被封禁了')
return
}
...
},true)
方案二:window.banner
为true
的时候展示一个全屏的最高层级(即:z-index:99999
)的div遮罩,遮住整个页面。
3.阻止默认事件
e.preventDefault()
这个没啥好说的
4.兼容性问题
addEventListener
--> firefox、Chrome、高版本IE、safari、opera
attachEvent
--> IE7、IE8,除了政府网站,大部分公司不会再去针对他们做兼容了
IE浏览器里没有事件捕获,只有事件冒泡
针对兼容性做一下封装
class BomEvent{
constructor(element){
this.element = element
}
addEvent(type,handler){
if(this.element.addEventListener){
this.element.addEventListener(type,handler,false)
} else if (this.element.attachEvent){
this.element.attachEvent(`on${type}`,function(){
// 这是处于ie7或8的一个环境,不支持箭头函数,所以这里做一下绑定
handler.call(element)
})
} else {
this.element[`on${type}`]=handler
}
}
removeEvent(type,handler){
if(this.element.removeEventListener){
this.element.removeEventListener(type,handler,false)
}else if(this.element.detachEvent){
this.element.detachEvent(`on${type}`,handler)
}else{
this.element[`on${type}`] = null
}
}
}
// IE浏览器里没有事件捕获,只有事件冒泡
function stopPropagation(event){
if(event.stopPropagation){
event.stopPropagation() //标准w3c浏览器
}else{
event.cancelBubble = true // IE
}
}
function preventDefault(event){
if(event.preventDefault){
event.preventDefault()
}else{
event.returnValue = false
}
}
二:浏览器请求相关内容Ajax与fetch
原生ajax:
const xhr = new XMLHttpRequest()
xhr.open('GET','http://domain/service') //建立连接,并没有发送请求
//监听状态
xhr.onreadystatechange = function(){
// 表示请求还没有完成
if(xhr.readyState !== 4){
return
}else if(xhr.status === 200){
// 请求成功
console.log(xhr.responseText)
}else{
//报错了
console.error(`HTTP error,status=${xhr.status},errorText = ${xhr.statusText}`)
}
}
// 超时时间
xhr.timeout = 3000
xhr.ontimeout = ()=>{
console.log('请求超时')
}
// 上传进度条
xhr.upload.onprogress = p => {
const percent = Math.round((p.loaded / p.total)*100) + '%'
}
xhr.send() //发送请求
fetch:浏览器新增的fetch,内部封装了promise
fetch('http://domain/service',{
method: 'GET',
credentials: 'same-origin' // 同域的请求会带上cookie
}).then(response => {
if(response.ok){
//请求成功
return response.json()
}
throw new Error(' http error')
}).then(json => {
console.log(json)
}).catch(error => {
console.error(error)
// 统一的错误管理
// 接收fetch出现的错误
// 接收请求错误信息
})
fetch
不能直接通过catch
来获取请求错误信息,而是要通过判断response.ok
来返回出错误信息再通过catch
来抓到错误信息
fetch
自身不支持设置请求超时,我们自己来封装一个
function fetchTimeout(url,init.timeout=3000){
return new Promise((resolve,reject)=>{
fetch(url,init).then(resolve).catch(reject)
setTimeOut(reject,timeout)
})
}
中断一个请求
//用于中断请求
const controller = new AbortController()
fetch('http://domain/service',{
method: 'GET',
credentials: 'same-origin', // 同域的请求会带上cookie
signal:controller.sianal
}).then(response => {
if(response.ok){
//请求成功
return response.json()
}
throw new Error(' http error')
}).then(json => {
console.log(json)
}).catch(error => {
console.error(error)
})
controller.abort()//用于中断请求
请求头request-header
referer
:表示来源,你是从哪一个页面过来这个页面的,标识访问路径
user-agent
:用来判断环境,设备标识
响应头response-header
access-control-allow-origin
:跨域
用于做域名限制,跨域
access-control-allow-origin:/* ,不做限制
-
content-encoding: 是一个实体消息首部,用于对特定媒体类型的数据进行压缩。当这个首部出现的时候,它的值表示消息主体进行了何种方式的内容编码转换。这个消息首部用来告知客户端应该怎样解码才能获取在
Content-Type
中标示的媒体类型内容。<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="js" cid="n20" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
Content-Encoding: identity
Content-Encoding: br</pre> set-cookie:响应首部
Set-Cookie
被用来由服务器端向客户端发送 cookie。
status状态码
响应分为五类:信息响应(100
–199
),成功响应(200
–299
),重定向(300
–399
),客户端错误(400
–499
)和服务器错误 (500
–599
)。
请求成功。成功的含义取决于HTTP方法:
GET:资源已被提取并在消息正文中传输。
HEAD:实体标头位于消息正文中。
POST:描述动作结果的资源在消息体中传输。
TRACE:消息正文包含服务器收到的请求消息
该请求已成功,并因此创建了一个新的资源。这通常是在POST请求,或是某些PUT请求之后返回的响应。
重定向。
被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。
临时重定向。
请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。
304:协商缓存,服务器文件未修改
面试题
vue/react
创建的单页面应用(spa),生成的index.html
如果要做缓存应该做什么缓存?
答:协商缓存