JavaScript的组成
- ECMAScript
ECMAScript定义了JavaScript的语法规范,是JavaScript的核心,描述了它的基本语法和数据类型。
ECMAScript是一套标准,与具体实现无关。
- 各版本主要实现
ES6(ES2015)
/* * let, const, class, modules, arrow functions, template string, destructuring, default, rest argument, binary data, promise等 */
ES7(ES2016)
/* * 完善ES6规范,求幂运算符*,array.prototype.includes等 */
ES8(ES2017)
/* * 原子,并发,Object.values/Object.entries,字符串填充,await/asyn等 */
- WebAPI
- 作用
WebAPI是浏览器提供的一套用于操作网页的API,可以让用户非常轻易地操作页面的元素以及浏览器的一些功能
- 内容
主要由
BOM
和DOM
两部分组成
- BOM
用于操作浏览器的一套API,可以借此使用浏览器窗口相关功能,如弹出框、控制浏览器跳转、获取分辨率等等
- DOM
用于操作页面元素的一套API,将HTML当做一个文档树,使用DOM提供的API就可以对树上的节点进行操作
DOM
几个概念
- 文档
document,指整个页面
- 节点
node,页面中的任何内容,如文字、属性、标签、注释等
- 元素
element,指标签节点
属性和方法
- 页面标签和DOM对象
- 固有属性
对于页面标签的固有属性,它们和DOM对象的属性是一一对应的,因此可以通过修改DOM对象的属性来间接实现对页面元素固有属性的修改
- 自定义属性
指用户在页面标签中自定义的属性,这些属性都是非固有属性,通常用于存储数据
自定义属性无法使用document对象去获取,且获取的DOM对象,无法直接使用box.自定义属性名
方式来获取自定义属性,需要使用以下api
来操作自定义属性
box.getAttribute("属性名")
可以获取固有/自定义属性,不存在则返回
null
box.setAttribute("属性名", "属性值")
设置属性值,可以是固有/自定义属性
box.removeAttribute("属性名")
移除属性,可以是固有/自定义属性
- 获取元素的方法
getElementById()
- 说明
通过id获取标签节点,即获元素对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <img id="demo" src="./static/img/test.png" alt="图片" title="图片"/> <script> let img = document.getElementById("demo"); setTimeout(() => { img.title = "美女"; }, 3000); </script> </body> </html>
- 注意
- 如果id不存在,则返回null,此时要注意不能再使用属性或方法,因为
null.onclick
等将会报错- 部分浏览器对于含有id属性的元素,可以直接进行使用而不用先对该元素进行获取,但是不规范,所以不推荐
getElementsByTagName()
- 说明
指通过标签名来获取元素的DOM对象,由于相同的标签往往不止一个,所以获取到的返回值将是一个伪数组
- 注意
如果标签不存在,一样会返回一个长度为0的伪数组
getElementsByClassName()
- 说明
和
getElementsByTagName()
的区别在于是通过类名来获取元素对象
- 注意
存在兼容性问题,IE678不支持该方法,一般不会用
getElementsByName()
- 说明
通过
name
属性来获取元素,返回值一样也是个伪数组
- 注意
从
name
属性可知,此方法仅限于获取表单元素
- 通过CSS选择器来获取元素
- 说明
document.querySelector("selector"); //只会获取到一个元素,即使用的是如标签选择器等可以获取多个元素的选择器,最终也只会获取第一个元素 document.querySelectorAll("selector"); //可以获取多个元素,且无论获取到多少个,都将返回一个伪数组 // 当然,也可以是任意DOM对象:box.querySelectorAll
- 注意
H5提供的新方法,IE678不支持
- 其他方法
document.getSelection()
获取选中的元素
- 常用属性
- 文本操作属性
innerText
- 说明
由IE浏览器设计出来的属性,获取到的是标签中的纯文本内容
- 兼容性问题
低版本的火狐浏览器不支持该属性,且火狐浏览器自身也设计了一个
textContent
属性,该属性对于IE678同样不支持,当然,对于现代浏览器,这两种属性都能支持
- 兼容性处理
function getText(element) { if(typeof element.innerText == "string") return element.innerText; else return element.textContext; }
innerHTML
- 说明
获取的是标签中的所有内容,即还包含了其中的子标签
- 和
innerText
的共同点
box.innerText/innerHTML = "纯文字"
结果都将会把box
元素中的所有内容(包括其中的子标签),一律替换为 “纯文字”
- 和
innerText
的不同点
box.innerText/innerHTML = "<p>文字</p>"
,innerText
会将p标签当做文字处理,而innerHTML
会把其解析成一个子标签,所以处于安全考虑,建议优先使用innerText
- style属性
- 说明
style
是页面标签中的一个固有属性,而对于DOM,所有的DOM对象也对应有一个style
对象,对象中的属性就对应页面标签的行内样式的所有可用属性box.style.backgroundColor = "pink"; //注意要使用驼峰式写法
- 注意
style对象操作的是行内样式,即一方面它无法获取非行内样式,另一方面它设置的是行内样式所以优先级较高
- document的几个特殊属性
document.body
:获取body元素document.head
:获取head元素document.title
:直接获取/修改title的内容document.documentElement
:获取html根标签元素
DOM文档树
- 节点
标签中的属性属于标签自身的属性节点,标签中的文本属于标签的文本子节点,即页面中的任何内容都是节点,包括文本内容、标签、注释、属性等
- 节点的查找
<ul> <!--注释--> <li>第1个li元素</li> <li id="two">第2个li元素</li> <li>第3个li元素</li> </ul>
- 子节点相关API
ul.chlidNodes
获取所有(直接)子节点,共9个,即ul有9个子节点:换行、注释、换行、li标签、换行、li标签、换行、li标签、换行
ul.children
只获取(直接)子节点中的标签节点,共3个,即三个li标签子节点,和
querySelectorAll
的区别如下:let lis_children = ul.children; let lis_queryAll = ul.querySelectorAll("ul > li"); btn.onclick = function(){ let li = document.createElement("li"); ul.appendChild(li); //children是动态集合,即会同步后续添加的li,而querySelectorAll则是静态集合,即永远是原来的那些li }
ul.firstChild
ul.lastChild
获取第一个/最后一个(直接)子节点,结果均为换行文本子节点
ul.firstElementChild
ul.lastElementChild
获取第一个/最后一个(直接)子节点中的标签节点,结果均为li标签子节点
- 兄弟节点相关API
two.previousSibling
two.nextSibling
获取上一个兄弟节点:第2个li和第1个li之间的换行文本节点;获取下一个兄弟节点:第2个和第3个li之间的换行文本节点
two.previousElementSibling
two.nextElementSibling
获取上一个兄弟标签节点:li标签节点(<li>第1个li</li>);获取下一个兄弟标签节点:li标签节点(<li>第3个li</li>)
- 父节点相关API
two.parentNode
获取父节点,即ul节点
- 节点的三个重要属性
nodeName
:节点名称nodeType
:节点类型返回值是个数字,其中1表示标签节点
nodeValue
:节点的值
- 节点的方法
parent.appendChild(node)
添加一个子节点为最后子节点,如果该子节点本身就是页面中的一个元素,则相当于是一个移动操作
parent.insertBefore(child, refChild)
添加一个子节点child到指定子节点refChild之前,如果refChild为undefined,则等同于
appendChild
操作
node.cloneNode(deep)
deep为布尔类型,表示是否需要进行深拷贝(不仅复制标签自身,还包括其中的子节点),默认false
parent.removeNode(child)
移除指定子节点
- 创建节点的几个方式
document.write()
可以解析标签(相当于实现了节点的创建),需要注意的是,如果页面已经加载完毕,再去执行此方法时(如由事件触发时),其写入的内容会将原来页面中body的内容覆盖,原因是页面从上往下加载时,会开启一个文档流,当加载完成时该文档流随之关闭,而执行该方法需要使用文档流,如果已关闭则会自己重新开启一个,以至将原来文档流内容覆盖了
innerHTML
一样可以解析标签,详情见文本属性
document.createElement("tagName")
和克隆一样,会在内存中创建一个标签节点,不会影响原有内容,推荐使用
let h1 = document.createElement("h1"); h1.innerText = "h1"; box.appendChild(h1);
AJAX
- 简介
Asynchronous Javascript And Xml,本质是基于HTTP协议,通过JS的XMLHttpRequest对象实现的异步请求。
AJAX和 a标签 或 form表单提交 或 直接输入网址 一样,都是一种用于请求后台的方式,区别在于,其他的请求方式都会刷新当前页面,而AJAX可以在不刷新页面的情况下请求服务器,从而实现页面的局部数据更新
XMLHttpRequest
一个浏览器的内置对象,用于与服务器进行通信(交换数据)
xhr.getResponseHeader(key); //获取指定的响应头中的值 xhr.getAllResponseHeaders(); //获取响应头中所有信息
- get 和 post 请求
//创建XMLHttpRequest对象 let xhr = new XMLHttpRequest(); //设置请求行 xhr.open('get', 'get接口地址'); //get请求 xhr.open('post', 'post接口地址'); //post请求 //设置请求头 xhr.setRequestHeader('context-type', 'text/html'); //post方法必须设置请求头 //发送请求:参数为请求体 xhr.send(null); //get无请求体 xhr.send("name=zs&age=12"); //post可以设置请求体(查询字符串) //监听请求状态 /** * xhr.readyState * 0:请求未初始化,即还没有调用open()方法 * 1:请求已建立,但还没有发送,即还没有调用send()方法 * 2:请求已发送,正在处理中 * 3:请求正在处理中,通常响应中已有部分数据可用了,但是服务器还没有完成 * 4:响应已经完成 */ xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { console.log(xhr.responseText); } }
XMLHttpRequest 2.0
- 支持超时时间
//设置在xhr.open()方法的后面 xhr.timeout = 1000; //单位毫秒 xhr.ontimeout = function(){}; //当超时时触发
- FormData
FormData是2.0新增的一个对象,用于管理表单数据,且表单中如果有
input:file
时,还将支持文件上传//指定要管理的表单 let fd = new FormData(document.querySelector("form")); //可以添加非表单中的额外的数据 fd.append('num', 1000000); let xhr = new XMLHttpRequest(); //使用FormData,必须用post请求 xhr.open("post", "..."); //使用FormData,post请求可以不设置请求头(1.0必须设) //直接发送fd实例即可 xhr.send(fd); xhr.onreadystatechange = function(){ //... };
对于文件的上传,还可以监听文件上传进度
//需在 xhr.send 方法前编写监听代码 xhr.upload.onprogress = function(e) { //e.total:文件总大小 //e.loaded:已上传文件大小 (e.loaded / e.total) * 100 + "%"; //设置成width值即可。 }
- 前后端传递数据的通用格式
XML
- 简介
EXtensible Markup Language,即可扩展标记语言,和HTML的区别在于,HTML主要用于展示数据,且HTML的标签都是已经预定义的,而XML则主要用于存储和传输数据,其标签是可扩展、可自定义的
- 语法规范
- 第一行必须是版本信息
- 必须有且仅有一个根元素
- 标签中不可有空格,且不可以以数据 或 . 开头,并区分大小写
- 不可以交叉嵌套,且都是双标签,如果是单标签则必须闭合
- 属性为双引号(浏览器会自动修正为双引号)
- 注释同HTML
- 示例
后端需设置 Content-type 为 text/xml
<?xml version="1.0" encoding="UTF-8"?> <root> <person id="1"> <name>zhangsan</name> <sex>male</sex> <age>19</age> <hobby>football</hobby> <book> <type>comedy</type> <name><<love>></name> </book> </person> <person id="2"> <name>lisi</name> <sex>female</sex> <age>21</age> </person> </root>
JSON
Javascript Object Notation,即js对象标记
/** * 将json字符串转为json数据 * JSON.parse(text, [, reviver]) * text:必填,一个有效的json字符串 * reviver:选填,可以理解为是个map算子,对象的每个成员都会调用此函数 */ let jsonStr = '{"name":"zhangsan","sex":"male","address":"henan"}'; let jsonData = JSON.parse(jsonStr, ((key, value) => { if (key === "sex" && value === 'male') return "man"; else if (key === "sex" && value === "female") return "woman"; else return value; })); console.log(jsonData); //{ name: 'zhangsan', sex: 'man', address: 'henan' } /** * 将json数据转为json字符串 * JSON.stringify(value[, replacer[, space]]) * value:必填,代表一个json数据的对象或数组 * replacer:选填,可以是个函数或数组 * 如果是函数,效果类似上面的reviver函数; * 如果是个数组,存放key值,表示只保留这些key对应的key-value数据。 * space:选填,可以是空格,换行符,制表符等,表示文本在每个级别中进行缩进的形式,如果是数字则表示缩进的空格数 */ let jsonString1 = JSON.stringify(jsonData, (key, value) => { if (key === "sex" && value === 'man') return "male"; else if (key === "sex" && value === "woman") return "female"; else return value; }, '\t'); console.log(jsonString1); // 输出如下: // { // "name": "zhangsan", // "sex": "male", // "address": "henan" // } let jsonString2 = JSON.stringify(jsonData, ['sex', 'name'], ' '); console.log(jsonString2); // 输出如下: // { // "sex": "man", // "name": "zhangsan" // }
- ajax的同步形式
//由open方法的第三个参数决定,默认true,表示异步,如果修改为false,则表示同步 xhr.open("get", "./index.php", false);
- 命名空间与ajax的封装
- 命名空间
项目组分配给个人的一个"变量名",该变量全局唯一,用于解决命名冲突、命名全局污染问题
- AJAX的封装
let xxx = { ajax(obj) { let url = obj.url || location.href; let type = obj.type || 'get'; let args = this.argHandler(obj.args); let callback = obj.callback; let xhr = new XMLHttpRequest(); if (type === 'get') { url = url + "?" + args; args = null; } xhr.open(type, url); if (type === 'post') { xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded'); } xhr.send(args); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { callback && callback(); } } }, argHandler(args) { let res = []; if (args) { for (let key in args) { if (args.hasOwnProperty(key)) { res.push(key + '=' + args[key]); } } res = res.join('&'); } return res.length ? res : ""; } };
- ajax和绑定事件的失效问题
如果绑定事件的元素是通过AJAX动态生成的,很大可能存在执行到主线程中该元素事件时,该元素仍未生成问题,最终的现象就是该元素所绑定的事件不生效
该问题无法通过入口函数的方式解决,因为入口函数不会感知AJAX,可通过事件委托方式解决
BOM
- 简介
指浏览器对象模型, 提供了一套用于操作浏览器相关功能的API
- 主要内容
frames
history
用于操作浏览器历史记录的对象,常用以下属性和方法
history.back
后退,回到之前页
history.forward
前进,进入后一页
history.go(数字)
前进/后退指定页数,数字为正表示前进,为负则是后退
location
相当于浏览器地址栏对应的对象,常用一下属性和方法
location.href
获取到当前页面地址栏中的完整链接地址
location.href = "http:/..."
跳转到指定页面
location.href = ""
空串表示刷新当前页面
location.reload()
表示刷新页面
location.hash
获取url的锚点,即#及之后的内容
location.host
获取 ip + port
location.hostname
获取 ip
location.port
获取 port
location.pathname
获取 port 之后的内容:
/文件路径.html
location.search
获取 ? 开始 + 参数
location.protocal
获取协议部分,如 http
location.origin
获取 协议 + ip + port
navigator
用于获取客户福安相关信息的对象
navigator.userAgent
获取用于代理信息,包括浏览器版本、操作系统、浏览器内核等内容,不过可能会罗列出所有可能的浏览器,所以信息不会很准确,而且用于可以修改浏览器的这些信息
screen
用于获取屏幕相关信息的对象,常用以下属性和方法
screen.width
获取电脑屏幕的宽度,是一个固定值
screen.height
获取电脑屏幕的高度,是一个固定值
screen.availWidth
获取浏览器可以占用的总宽度
screen.availHeight
获取浏览器可以占用的总高度
window对象
- 简介
是js中的一个全局对象,一个顶级对象,BOM和DOM中的属性和方法都输与window,比如常用的document,alert(),console.log()等,只不过对于window对象中属性和方法的使用可以省略window的书写
window.onload
入口函数,在页面以及外部资源,如图片、css、js等加载完成之后才会执行,并且一个页面只有一个入口函数会生效,即使写了多个,后面的会将前面的覆盖
- 常用方法
open()
用于打开一个窗口
window.open(url, [name], [features]) /* * url:打开的窗口的地址 * name:可选,新窗口名称,一方面可以通过window.name获取该名称,另一方面他还有个作用,就是对于点击打开窗口,浏览器会先通过name判断是否已经打开过该窗口,如果打开过则会在已打开的该窗口中执行刷新操作,而非重复地打开 * features:可选,用于指定新窗口的特性,如指定窗口的大小等 * 返回值:返回该创建的窗口对象,后续可通过此对象对该窗口进行关闭 */
let w = window.open("http://www.baidu.com", "百度", "width=300, height=300, top=100, left=100"); w.close(); //关闭此窗口
close()
window.close()
表示关闭当前窗口
getComputedStyle()
获取在元素计算后的样式,即元素上真正起着效果的样式
window.getComputedStyle(元素, 伪类); /* * 元素:要获取样式的目标元素 * 伪类:表示获取元素伪类的样式,一般不需要,填写null即可 * 返回值:返回的是一个对象,其中包含了该元素所有的计算样式 * 兼容性:IE678不兼容,使用的是element.currentStyle[attr] */
- 延时器
let timer = setTimeout(function() { //... }, delay); /* * delay:延时的时间,单位毫秒,该时间并不准确,只能说至少需要这些时间,因为事件队列中可能还有其他回调函数要处理,或者是还有很多同步代码要执行 */ clearTimeout(timer); //清除指定的延时器
- 定时器
let interval = setInterval(function(){ //... }, interval); /* * interval:定时器的时间间隔,单位毫秒 */ clearInterval(interval); //清除指定的定时器 //定时器中的this指向问题 setInterval(function(){ console.log(this); //定时器中的this指向的是window }, 1000);
事件
- 简介
javascript是基于事件驱动的,而事件则是一种 触发-响应 机制,有三要素构成:
- 事件源
触发事件的元素
- 事件名称
如
click
为点击事件的名称
- 事件处理程序
事件触发后要执行的代码
- 事件中的 this
指向的是触发事件的那个元素
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> input[type="button"] { width: 50px; height: 50px; line-height: 50px; text-align: center; background-color: salmon; border: none; outline-style: none; } .hotpink { background-color: hotpink!important; } </style> </head> <body> <input type="button" value="变色" /> <input type="button" value="变色" /> <input type="button" value="变色" /> <input type="button" value="变色" /> <input type="button" value="变色" /> <script> let btns = document.getElementsByTagName("input"); for(let i = 0; i < btns.length; i++) { btns[i].onclick = function () { console.log(this) this.className = "hotpink"; //注意此处不能用btns[i].className的方式,因为当点击时此处代码已经遍历完成,i的值已经成了最大值,只会最后一个按钮会变色 } } </script> </body> </html>
- 常用事件
onclick
:点击事件
ondblclick
:双击事件
onmousemove
:鼠标页面移动事件
onmousewheel
:鼠标滚轮事件
onmouseover
:鼠标移入事件
onmouseout
:鼠标移出事件
onmouseenter
:鼠标进入事件(不支持事件冒泡)
onmouseleave
:鼠标离开事件(不支持事件冒泡)
onfocus
:聚焦事件
onblur
:失焦事件
onkeydown
:键盘按下事件
onkeyup
:键盘弹起事件
onmousedown
:鼠标按下事件
onmouseup
:鼠标弹起事件
onscroll
:滚动条滚动事件
onresize
:窗口大小变动事件
onchange
对于 <input>, <select>, 和 <textarea>,当用户输入发生变化(value属性发生了改变)时触发,常用于全选/反选,以及图片预览等
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <input type="file" multiple> <img src="#" alt="none" width="200px" /> <script> let img = document.querySelector("img"); /*当选中文件时就会触发*/ document.querySelector('input[type="file"]').onchange = function () { let file2show = this.files[0]; img.src = URL.createObjectURL(file2show); } </script> </body> </html>
oninput
H5新增的表单标签的一个事件,用户每输入一次都会触发,注意由于
jQuery
尚未对此事件进行封装,所以只能用$box.on("input", fn)
的方式进行注册,而非$box.input(fn)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <label for="text">请输入:</label> <input type="text" id="text"> <script> document.querySelector("input").oninput = function () { console.log(this.value); } </script> </body> </html>
- Event
- 说明
每当触发一个事件的时候,都会产生一个对应的
Event
,即事件对象,该对象中存储着本次事件中的相关信息,包括触发事件的元素、事件的类型等等
- 获取事件对象
存在兼容性问题
- 对于现代浏览器
只需要在事件处理函数中指定一个形参即可
document.onclick = function(event) { console.log(event); //event即为事件对象,其名称自定义 }
- 对于IE678
需要使用事件对象属于
window
的一个属性document.onclick = function() { console.log(window.event); //window.event即为事件对象 }
- 兼容性处理
document.onclick = function(event) { let event = event || window.event; }
- 常用属性
keyCode:按下的键对应的键盘码(回车键:13;空格键:32) altKey:布尔,鼠标是否 按住了alt键 ctrlKey shiftKey clientX:鼠标距离可视区左侧的距离 clientY:鼠标距离可视区上侧的距离 pageX:鼠标距离页面左侧的距离 pageY:鼠标距离页面上侧的距离 offsetX:鼠标相对于父元素左侧的距离 offsetY:鼠标相对于父元素上侧的距离 screenX:鼠标距离屏幕左侧的距离 screenY:鼠标距离屏幕上侧的距离
- 事件注册与解绑
on
button.onclick = function(){}; button.onclick = null; //解绑事件 /* * 注意:如果相同的元素注册了多个相同的事件,后面的会覆盖之前的,且on方式注册的事件只能在事件冒泡阶段被触发 */
addEventListener
addEventListener(event, fn, useCapture)
:注册事件
removeEventListener(event, fn, useCapture)
:移除事件let fn = function(){//...} button.addEventListener("click", fn); /* * event:事件名(去除on前缀) * fn:事件处理函数 * useCapture:可选,表示是否使用事件捕获,默认false * 注意:不存在覆盖问题,另外IE678不支持 */ button.removeEventListener("click", fn}); /* * event:事件名(去除on前缀) * fn:指定要移除的函数(对应addEventListener的fn参数),注意addEventListener中的fn不能是直接用的匿名/具名函数表达式,只能是传入一个函数名,否则addEventListener所添加的事件将无法移除 * useCapture:可选,表示是否使用了事件捕获,默认false * 注意:不存在覆盖问题,另外IE678不支持 */
- 事件流
- 事件冒泡和事件捕获
假设有这样的标签结构html > body > div > p
,如果点击了p
标签,此时这层结构的点击顺序就对应了事件冒泡和事件捕获
- 冒泡型事件
在IE浏览器中,点击顺序默认为
由p到html
,这种顺序称为事件冒泡
- 捕获型事件
在火狐浏览器中,点击顺序默认为
由html到p
,这种顺序称为事件捕获,注意IE678不支持事件捕获
- 事件的传播
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .outer { width: 50px; height: 50px; background-color: pink; position: relative; } .middle { width: 30px; height: 30px; background-color: hotpink; } .inner { width: 10px; height: 10px; background-color: deeppink; } div div { position: absolute; top: 50%; left: 50%; transform: translateY(-50%) translateX(-50%); } </style> </head> <body> <div class="outer"> <div class="middle"> <div class="inner"></div> </div> </div> <script> let divs = document.getElementsByTagName("div"); //... </script> </body>
- 事件冒泡的输出顺序
for (let i = 0; i < divs.length; i++) { divs[i].onclick = function () { console.log(this); //inner middle outer } //所有div注册的都是冒泡事件 }
- 事件捕获的输出顺序
for (let i = 0; i < divs.length; i++) { divs[i].addEventListener("click", function () { console.log(this); //outer middle inner }, true); //所有div注册的都是捕获事件 }
- 注意
事件传播与事件是否注册无关,事件注册只是能在对应阶段上执行事件处理函数
- 阻止事件传播
for (let i = 0; i < divs.length; i++) { console.log(divs[i]); divs[i].addEventListener("click", function (e) { e.stopPropagation(); //阻止后,点击了谁只会输出谁 console.log(this); }, false) }
- 事件的三个阶段
- 三个阶段的执行顺序
- 事件捕获阶段
- 事件目标阶段
在此阶段将执行对应元素的事件处理函数
- 事件冒泡阶段
- 注册何种类型事件的含义
如注册的是冒泡事件,指该元素的对应事件将在冒泡阶段触发,如下代码,点击
inner
后,输出顺序为:outer > inner > middlelet outer = document.getElementsByClassName("outer")[0]; let middle = document.getElementsByClassName("middle")[0]; let inner = document.getElementsByClassName("inner")[0]; inner.addEventListener("click", function () { //inner注册捕获事件 console.log(this) }, true); middle.addEventListener("click", function () { //middle注册冒泡事件 console.log(this) }, false); outer.addEventListener("click", function () { //outer注册捕获事件 console.log(this) }, true);
同源和跨域
- 概念
- 同源
指来自同一服务器的资源,它们 协议相同、域名相同、端口相同
- 跨域
不是同源,即为跨域,跨域主要是存在安全问题,对此浏览器做出了以下限制
- 不能共享cookie
- 不能互相操作dom
- 不能发送ajax
- 关于ajax的跨域限制
ajax无法跨域请求服务器,实则是浏览器对XMLHttpRequest对象做了限制,但是使用src/href等属性依旧可以跨域请求图片、js文件、css文件等,甚至,还可以访问php文件,而且通过拼接get参数,还能向跨域的php文件传递数据
可以通过jsonp
、CORS
、反向代理等方式解决ajax的跨域限制
- jsonp
jsonp是指就是通过使用
script:src
的方式实现的跨域请求,不过只能发送get请求,且需要后台配合
后台代码const express = require('express'); const router = express.Router(); router.get('/index', (req, res) => { let params = req.query; console.log(params); //{ fn: 'show' },前端将函数名传给后端,且彼此先定义好将函数名放在一个字段,如此例中的fn字段 res.send(`${params['fn']}("我去后端走了一趟")`); //后端将函数名取出,并拼接上(参数),返回给前端后,浏览器就会调用该方法 }); module.exports = router;
前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> div { width: 100px; height: 100px; background-color: hotpink; display: none; } </style> </head> <body> <div></div> <script> let div = document.querySelector('div'); function show(msg) { setTimeout(function () { div.style.display = 'block'; div.innerText = msg; }, 3000); } </script> <script src="http://localhost:56789/index?fn=show"></script> </body> </html>
- CORS
Cross-Origin Resource Sharing,跨域资源共享,此方法只需在后端设置一个允许请求访问的响应头即可
header("Access-Control-Allow-Origin", "*"); //允许所有请求访问 header("Access-Control-Allow-Origin", "http://localhost"); //允许指定的请求路径访问服务器(端口未设置则默认为80)
- 反向代理
跨域限制只是存在于浏览器和服务器,服务器之间是不存在的,反向代理大致的意思就是,通过配置一个代理,这个代理和代码之间是同源的,代码的接口请求不直接发给后台,而是发给代理,由代理转发给后台接口中,这样就解除跨域限制问题了,常用的有nginx、axios + vue.config.js(vue)等
import axios from 'axios' //设定一个自定义的前缀,用于劫持时的标识 axios.get("/myapi/movie/in_theaters").then(res => { console.log(res) });
//在根路径中创建配置文件vue.config.js,并配置代理 module.exports = { devServer: { proxy: { '/myapi': { //1.劫持 /myapi前缀的请求 target: 'https://douban.uieee.com/v2', //2.拼接 成https://douban.uieee.com/v2/myapi/movie/in_theaters pathRewrite: {'^/myapi': ''}, //3.删除 成https://douban.uieee.com/v2/movie/in_theaters secure: false, //只有当目标的地址是https请求时才需要,用于解析目标路径的端口用 changeOrigin: true //是否可进行跨域 } } } };
小知识
- script标签的放置位置
不将<script>标签放在<head>标签中,否则可能无法获取到内容,因为对代码的解析是从上往下进行的,所以最好放在<body>中最后的位置
- console.dir()
和console.log不同的是可以展开对象
- 表单中的布尔类型属性
- input/button:disabled
- input[radio/checkbox]:checked
- select-option:selected
return false
:阻止a标签跳转<a id="link" href="http://www.baidu.com">百度</a> <scripy> var link = document.getELementById("link"); link.onclick = function(){ return false; } </script>
- 几个按钮
input:submit
会刷新页面,如果用于发送ajax,需要先阻止浏览器默认行为
input:button
不会刷新页面,适合用于发送ajax请求
button
同
input:submit
- 图片高度布局错乱问题
图片从服务器获取速度受网速影响,故而小于代码运行速度,所以可能当只加载了一部分图片时,导致代码获取的图片高度非该图片正确高度,导致高度布局错乱
对此可以使用入口函数;另外若使用了模板引擎,可以现在模板引擎中先设定图片的宽高,即先占据好位置