此处的知识点为常用知识点, 了解更多可以查看这篇优秀的文章 《2021JavaScript知识汇总》 ( 也是我写的 - _ - )
1、DOM
⑴、本质
HTML 是一个有既定标签标准的 XML 格式,标签的名字、层级关系和属性,都被标准化(否则浏览器无法解析)。同样,它也是一棵树。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<p>this is p</p>
</div>
</body>
</html>
基于以上需求,浏览器就需要把 html 转变成 DOM,html 是一棵树,DOM 也是一棵树。可以认为 DOM 就是 JS 能识别的 html 结构,一个普通的 JS 对象或者数组。
⑵、常用方法
DOM节点操作
- getElementById() 通过元素
ID
属性值选取节点, 返回单一对象 - getElementsByClassName() 通过元素
类名
属性值选取节点, 返回包含该对象的集合 - getElementsByName() 通过元素
name
属性值选取节点, 返回包含该对象的集合 - getElementsByTagName() 通过
标签名
选取指定元素节点, 返回包含该对象的集合 - document.querySelector() 通过
选择器
得到元素 - document.querySelectorAll() 通过
选择器
得到元素数组
DOM结构操作
- 节点的创建 document.createElement()
- 节点的获取( 父元素 / 子元素 ) 获取的节点 . parentElement / childNodes
- 节点的删除 removeChild ( 要删除子节点 )
- 节点的克隆 cloneNode()
⑶、属性 prototype & Attribute 的区别
property 的获取和修改,是直接改变 JS 对象,而 Attibute 是直接改变 html 的属性。两种有很大的区别
prototype
var pList = document.querySelectorAll('p')
var p = pList[0]
console.log(p.style.width) // 获取样式
p.style.width = '100px' // 修改样式
console.log(p.className) // 获取 class
p.className = 'p1' // 修改 class
// 获取 nodeName 和 nodeType
console.log(p.nodeName)
console.log(p.nodeType)
Attribute
var pList = document.querySelectorAll('p')
var p = pList[0]
p.getAttribute('data-name')
p.setAttribute('data-name', 'imooc')
p.getAttribute('style')
p.setAttribute('style', 'font-size:30px;')
⑷、实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DOM实例</title>
</head>
<body>
<div id="div1" class="class-div1">
<p id="p1" data-name="p1-data-name">this is p1</p>
<p id="p2">this is p2</p>
</div>
<div id="div2">
<p id="p3">this is p3</p>
<p id="p4">this is p4</p>
</div>
<style>
div{
margin: 20px;
padding: 20px;
background-color: #eee;
}
</style>
<script type="text/javascript">
// 通过元素`ID`属性值选取节点, 返回单一对象
var div1 = document.getElementById('div1')
console.log( div1 )
// 修改ID - property
console.log( div1.className )
div1.className = "class-div2"
console.log( div1.className )
// 修改data-name - attribute
var p1 = document.getElementById('p1')
console.log( p1.getAttribute( 'data-name' ) )
p1.setAttribute( 'data-name', 'p2-data-name' )
console.log( p1.getAttribute( 'data-name' ) )
// 通过`标签名`选取指定元素节点。返回包含该对象的集合
var divList = document.getElementsByTagName('div')
console.log( divList )
// 对象的个数
console.log(divList.length)
// 第 0 个对象
console.log(divList[0])
// 通过元素`类名`属性值选取节点, 返回包含该对象的集合
var containerList = document.getElementsByClassName('.container')
console.log( containerList )
// 通过`选择器`得到元素数组
var pList = document.querySelectorAll('p')
console.log( pList )
// !!!DOM结构操作
// 添加新节点
var p = document.createElement('p')
p.innerHTML = 'new p'
var div1 = document.getElementById('div1')
div1.appendChild(p)
// 移动已有节点
var p4 = document.getElementById('p4')
var div1 = document.getElementById('div1')
div1.appendChild(p4)
// 获取父元素节点
var p3 = document.getElementById('p3')
var div1 = document.getElementById('div1')
var parent = div1.parentElement
console.log( parent )
console.log( p3.parentElement )
// 获取子元素节点
var div1 = document.getElementById('div1')
var child = div1.childNodes
console.log( child )
console.log( child[0].nodeType ) // text 1
console.log( child[1].nodeType ) // p 1
console.log( child[0].nodeName ) // text #text
console.log( child[1].nodeName ) // p #p
// 删除节点
var div1 = document.getElementById('div1')
var childNodes = div1.childNodes
// childNodes[0]是一个空text, 删除会报错
div1.removeChild(childNodes[1])
</script>
</body>
</html>
2、BOM
⑴、题目
- 如何检测浏览器的类型
- 拆解url的各部分
⑵、知识点
BOM(浏览器对象模型)是浏览器本身的一些信息的设置和获取,例如获取浏览器的宽度、高度,设置让浏览器跳转到哪个地址。
- navigator 浏览器
- screen 屏幕
- location 地址
- history 历史( 前进 后退 )
实例
// navigator
var ua = navigator.userAgent
var isChrome = ua.indexOf('Chrome')
console.log(isChrome)
// screen
console.log(screen.width)
console.log(screen.height)
// location
console.log(location.href) // 整个url
console.log(location.protocol) // 协议 'http:' 'https:'
console.log(location.host) // 域名
console.log(location.pathname) // 路径 '/learn/199'
console.log(location.search) // 参数 ?后面的
console.log(location.hash) // #后面就是哈希
// history
history.forward() // 前进
history.back() // 后退
3、事件
⑴、题目
- 编写一个通用的事件监听函数
- 描述DOM事件冒泡流程
- 对于一个无线下拉加载图片的页面,如何给每个图片绑定事件
⑵、知识点
①、事件绑定
var btn = document.getElementById('btn1')
btn.addEventListener('click', function (event) {
console.log('clicked')
})
②、通用事件绑定函数
// elem 元素、type 类型、fn 函数
function bindEvent(elem, type, fn) {
elem.addEventListener(type, fn)
}
var a = document.getElementById('link1')
// 对应 a是链接, 'click'是类型, function是函数
bindEvent(a, 'click', function(e) {
e.preventDefault() // 阻止默认行为
alert('clicked')
})
③、关于IE低版本的兼容性
IE 低版本是使用
attachEvent
来绑定事件的
④、事件冒泡
需求:仅绑定两个事件, 点击 激活 ,弹出 激活, 点击 取消, 弹出 取消
<body>
<div id="div1">
<p id="p1">激活</p>
<p id="p2">取消</p>
<p id="p3">取消</p>
<p id="p4">取消</p>
</div>
<div id="div2">
<p id="p5">取消</p>
<p id="p6">取消</p>
</div>
</body>
**实现**
var p1 = document.getElementById('p1')
var body = document.body
bindEvent(p1, 'click', function (e) {
// 阻止事件冒泡
e.stopPropatation()
alert('激活')
})
// 最终事件冒泡, 均会触发给body绑定的事件
bindEvent(body, 'click', function (e) {
alert('取消')
})
如果在p1
div1
body
中都绑定了事件,会根据 DOM 的结构,来冒泡从下到上挨个执行
。但是我们使用e.stopPropatation()
就可以阻止冒泡
⑤、代理
场景:一个
<div>
中包含了若干个<a>
,而且还能继续增加。怎么给所有的<a>
绑定事件
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
</div>
<button>点击增加一个 a 标签</button>
这里就会用到事件代理,我们要监听<a>
的事件,把具体的事件绑定到<div>
上,然后看事件的触发点,是不是<a>
var div1 = document.getElementById('div1')
div1.addEventListener('click', function (e) {
var target = e.target
if (e.nodeName === 'A') {
alert(target.innerHTML)
}
})
⑥、完善通用绑定事件的函数 ( 代理 )
在完善一下之前写过的通用事件绑定函数,加上事件代理
// elem 元素、type 类型、selector 选择器、fn 函数
function bindEvent(elem, type, selector, fn) {
// 如果只传递了三个参数 ( 未使用代理 )
if (fn == null) {
// 将 选择器 和 函数 调个位置
fn = selector
selector = null
}
elem.addEventListener(type, function (e) {
var target
if (selector) {
target = e.target
// 如果 target 满足这个选择器
if (target.matches(selector)) {
fn.call(target, e)
}
} else {
fn(e)
}
})
}
// 使用
// 使用代理
var div1 = document.getElementById('div1')
bindEvent(div1, 'click', 'a', function (e) {
console.log(this.innerHTML)
})
// 不适用代理
var a = document.getElementById('a1')
bindEvent(div1, 'click', function (e) {
console.log(a.innerHTML)
})
4、Ajax 跨域
⑴、题目
- 手动编写一个 ajax,不依赖第三方库
- 跨域的几种实现方式
⑵、知识点
①、XMLHttpRequest - Ajax核心API
// 通过 XMLHttpRequest() 声明一个对象
var xhr = new XMLHttpRequest()
// 打开 通过get的方式 请求地址 异步/ false是同步
xhr.open("GET", "/api", true)
// 会监听状态变化, 重新执行函数
xhr.onreadystatechange = function () {
// 如果 已完成
if (xhr.readyState == 4) {
// 如果 服务端返回的是 200
if (xhr.status == 200) {
// 弹出 服务端返回的内容
alert(xhr.responseText)
}
}
}
// 发送
xhr.send(null)
②、XMLHttpRequest - Ajax核心API
IE 低版本的兼容性问题,它使用
var xhr = new ActiveXObject("Microsoft.XMLHTTP")
创建
③、状态码说明
Ⅰ、readyState
- 0 - (未初始化)还没有调用send()方法
- 1 -(载入)已调用send()方法,正在发送请求
- 2 -(载入完成)send()方法执行完成,已经接收到全部响应内容
- 3 -(交互)正在解析响应内容
- 4 -(完成)响应内容解析完成,可以在客户端调用了
Ⅱ、status
- 2xx 表示成功处理请求, 如:200
- 3xx 需要重新定向, 浏览器直接跳转
- 4xx 客户端请求错误, 如:404
- 5xx 服务器端错误
④、跨域
Ⅰ、什么是跨域
- 浏览器有同源策略, 不允许 ajax 访问其他域接口
- 跨域条件:协议、域名、端口, 有一个不同就算跨域
- 例:https://www.csdn.net/?spm=1011.2124.3001.4476
:80
就是端口号, 没写就是通用端口
跨域跨域的三个标签
- < img src = xxx > // 部分网站会做处理
- < link href = xxx >
- < script src = xxx >
- < img > 用于打点统计, 统计网站可能是其他域
- < link > < script > 跨域使用CDN, CDN的也是其他域
- < script > 可以用与 JSONP
跨域注意事项:所有的跨域请求都必须经过信息提供方允许, 如果未经允许即可获得, 那是浏览器同源侧栏出现漏洞
Ⅱ、JSONP
访问http://coding.m.imooc.com/classindex.html
的时候,服务器端就不一定有一个classindex.html
文件,服务器可以拿到这个请求,然后动态生成一个文件,然后返回。
同理,<script src="http://coding.m.imooc.com/api.js">
也不一定加载一个服务器端的静态文件,服务器也可以动态生成文件并返回
实现原理
<script>
window.callback = function (data) {
// 这是我们跨域得到信息
console.log(data)
}
</script>
<script src="http://coding.m.imooc.com/api.js"></script>
// 以上将返回 callback({x:100, y:200})
// 这个js加载之后,就会执行内容,我们就得到内容了
Ⅲ、服务端设置 http header
需要在服务器端设置
response.setHeader("Access-Control-Allow-Origin", "http://localhost:8011"); // 第二个参数填写允许跨域的域名称,不建议直接写 "*"
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials", "true");