WebAPI学习笔记

JavaScript的组成

  1. ECMAScript

ECMAScript定义了JavaScript的语法规范,是JavaScript的核心,描述了它的基本语法和数据类型。

ECMAScript是一套标准,与具体实现无关。

  • 各版本主要实现
  1. ES6(ES2015)
/*
 * let, const, class, modules, arrow functions, template string, destructuring, default, rest argument, binary data, promise等
 */
  1. ES7(ES2016)
/*
 * 完善ES6规范,求幂运算符*,array.prototype.includes等
 */
  1. ES8(ES2017)
/*
 * 原子,并发,Object.values/Object.entries,字符串填充,await/asyn等
 */
  1. WebAPI
  • 作用

WebAPI是浏览器提供的一套用于操作网页的API,可以让用户非常轻易地操作页面的元素以及浏览器的一些功能

  • 内容

主要由BOMDOM两部分组成

  • 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("属性名")

移除属性,可以是固有/自定义属性

  • 获取元素的方法
  1. 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>
  • 注意
  1. 如果id不存在,则返回null,此时要注意不能再使用属性或方法,因为null.onclick等将会报错
  2. 部分浏览器对于含有id属性的元素,可以直接进行使用而不用先对该元素进行获取,但是不规范,所以不推荐
  1. getElementsByTagName()
  • 说明

指通过标签名来获取元素的DOM对象,由于相同的标签往往不止一个,所以获取到的返回值将是一个伪数组

  • 注意

如果标签不存在,一样会返回一个长度为0的伪数组

  1. getElementsByClassName()
  • 说明

getElementsByTagName()的区别在于是通过类名来获取元素对象

  • 注意

存在兼容性问题,IE678不支持该方法,一般不会用

  1. getElementsByName()
  • 说明

通过name属性来获取元素,返回值一样也是个伪数组

  • 注意

name属性可知,此方法仅限于获取表单元素

  1. 通过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)

移除指定子节点

  • 创建节点的几个方式
  1. document.write()

可以解析标签(相当于实现了节点的创建),需要注意的是,如果页面已经加载完毕,再去执行此方法时(如由事件触发时),其写入的内容会将原来页面中body的内容覆盖,原因是页面从上往下加载时,会开启一个文档流,当加载完成时该文档流随之关闭,而执行该方法需要使用文档流,如果已关闭则会自己重新开启一个,以至将原来文档流内容覆盖了

  1. innerHTML

一样可以解析标签,详情见文本属性

  1. 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则主要用于存储和传输数据,其标签是可扩展、可自定义的

  • 语法规范
  1. 第一行必须是版本信息
  2. 必须有且仅有一个根元素
  3. 标签中不可有空格,且不可以以数据 或 . 开头,并区分大小写
  4. 不可以交叉嵌套,且都是双标签,如果是单标签则必须闭合
  5. 属性为双引号(浏览器会自动修正为双引号)
  6. 注释同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>&lt;&lt;love&gt;&gt;</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

  • 主要内容
  1. frames
  2. history

用于操作浏览器历史记录的对象,常用以下属性和方法

  • history.back

后退,回到之前页

  • history.forward

前进,进入后一页

  • history.go(数字)

前进/后退指定页数,数字为正表示前进,为负则是后退

  1. 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

  1. navigator

用于获取客户福安相关信息的对象

  • navigator.userAgent

获取用于代理信息,包括浏览器版本、操作系统、浏览器内核等内容,不过可能会罗列出所有可能的浏览器,所以信息不会很准确,而且用于可以修改浏览器的这些信息

  1. 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是基于事件驱动的,而事件则是一种 触发-响应 机制,有三要素构成:

  1. 事件源

触发事件的元素

  1. 事件名称

click为点击事件的名称

  1. 事件处理程序

事件触发后要执行的代码

  • 事件中的 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:鼠标距离屏幕上侧的距离
  • 事件注册与解绑
  1. on
button.onclick = function(){};
button.onclick = null; //解绑事件
/*
 * 注意:如果相同的元素注册了多个相同的事件,后面的会覆盖之前的,且on方式注册的事件只能在事件冒泡阶段被触发
 */
  1. 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)
}
  • 事件的三个阶段
  • 三个阶段的执行顺序
  1. 事件捕获阶段
  2. 事件目标阶段

在此阶段将执行对应元素的事件处理函数

  1. 事件冒泡阶段
  • 注册何种类型事件的含义

如注册的是冒泡事件,指该元素的对应事件将在冒泡阶段触发,如下代码,点击inner后,输出顺序为:outer > inner > middle

let 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);

同源和跨域

  • 概念
  • 同源

指来自同一服务器的资源,它们 协议相同、域名相同、端口相同

  • 跨域

不是同源,即为跨域,跨域主要是存在安全问题,对此浏览器做出了以下限制

  1. 不能共享cookie
  2. 不能互相操作dom
  3. 不能发送ajax
  • 关于ajax的跨域限制

ajax无法跨域请求服务器,实则是浏览器对XMLHttpRequest对象做了限制,但是使用src/href等属性依旧可以跨域请求图片、js文件、css文件等,甚至,还可以访问php文件,而且通过拼接get参数,还能向跨域的php文件传递数据


可以通过jsonpCORS、反向代理等方式解决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

  • 图片高度布局错乱问题

图片从服务器获取速度受网速影响,故而小于代码运行速度,所以可能当只加载了一部分图片时,导致代码获取的图片高度非该图片正确高度,导致高度布局错乱


对此可以使用入口函数;另外若使用了模板引擎,可以现在模板引擎中先设定图片的宽高,即先占据好位置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值