主要内容:
- 跨域请求(广义、getCORS、JSONP)
- classList方法操作元素属性
- 自定义data属性
- 事件冒泡
- 事件代理(委托)
1. 跨域请求的基本知识
1-1. 背景知识
- 跨域:跨越不同的域名/网站的访问资源
- 我们主要是学习如何通过 js 脚本发起一个跨域请求
- 为了网站的安全,默认是禁止通过 js 脚本发起跨域请求,但可以进行伪装之类的。
- 同源策略: 保护网站的最基本的一道安全防线
1-2. 跨域资源
1-3. 同源策略
- 页面 URL 中的三要素完全相同,就认为同源
- 协议(http/https/ftp/…)相同
- 域名(domain)相同
- 端口(port)相同
- 同源
http://php.cn:80/course/1088.html
http://php.cn:80/wenda/18.html
- 非同源,协议不同
https://php.cn:80/wenda/18.html
- 非同源,域名主机
https://php88.cn:80/wenda/18.html
- 非同源,端口不同
http://php.cn:9098/abc.html
2. 广义的跨域请求,通过html标签发起
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <link rel="stylesheet" href="cdn...">
<script src="cdn..."></script> -->
<title>广义上的跨域请求,通过html标签发起的</title>
</head>
<body>
<!-- 当前该请求所在的页面的域名是: http://front.edu/ -->
<!-- 要访问资源所在的目标站点域名是:https://www.php.cn/ -->
<!-- 这就是一个最简单的跨域访问 -->
<a href="https://www.php.cn/">访问php中文网</a>
<!-- img完成了一个跨域请求,请求另外一个域名站点中的一张图片 -->
<img src="https://img.php.cn/upload/course/000/000/001/5f36339d51421830.png" alt="">
</body>
</html>
3. 通过CORS发起跨域请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>跨域请求之-跨域资源共享-CORS</title>
</head>
<body>
<button>跨域请求-CORS</button>
<h2></h2>
<script>
var btn = document.querySelector("button");
btn.addEventListener("click", getData, false);
// 1. 创建ajax对象
var xhr = new XMLHttpRequest();
// 之前这个更多是放到function中,但这次因为是两个function调用,因此就放出来,放到公共空间中。
function getData() {
// 2. 监听请求
xhr.addEventListener("readystatechange", getCORS, false);
// 3. 配置参数
xhr.open("get", "http://php.io/test1.php", true);
// 4. 发送请求
xhr.send(null);
}
function getCORS() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText);
document.querySelector("h2").innerHTML = xhr.responseText;
}
}
</script>
</body>
</html>
4. 通过jsonp来发起跨域请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>jsonp的背景知识</title>
</head>
<body>
<h3>jsonp的背景知识</h3>
<script>
// 动态生成script标签,并加载一个外部的js文档
// 1. 生成一个script元素
var script = document.createElement("script");
// 可选
script.type = "text/javascript";
// 2. 添加src,指定外部请求的文档资源。这个地方可以是js,也可以是txt,也可以是下面的带回调的php。
// 没报这个CORS错误,说明其他类型的文件都可以访问
script.src = "http://php.io/test1.php?id=2&jsonp=回调handle";
// 3. 把script元素添加到文档(页面中)
document.head.appendChild(script);
// 此时通过script标签发起一个跨域请求,轻松的绕过了同源策略的限制
function handle() {
//...
}
// 这行函数的调用代码必须要在服务器脚本上生成,以字符串方式返回
handle(data);
</script>
</body>
</html>
- 上面调取的是php_io下的test1.php文件
<?php
// header('Access-Control-Allow-Origin:http://front.edu');
// 请资源开放给所有的请求
header('Access-Control-Allow-Origin:*');
// 允许以后带着cookie发起请求
// header('Access-Control-Allow-Origin:true');
echo '跨域脚本返回的数据';
// 下面这行是用来刷新缓存的。
flush();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>跨域请求-JSONP</title>
</head>
<body>
<button>跨域请求-JSONP</button>
<script>
var btn = document.querySelector("button");
btn.addEventListener("click", function () {
// 动态生成script标签,发起jsonp请求
var script = document.createElement("script");
// url中的最后一个键值对,必须是一个回调,需要跟test2.php中的名称一致(jsonp,但也可以是其他名字)
script.src = "http://php.io/test2.php?id=3&jsonp=handle";
// 这块是将外部请求生成的js内容放到什么位置。这个地方是放到了head下,其他地方也可以放。
document.head.appendChild(script);
});
// 从php中拿到信息后进行解析,并渲染到前端去。
// 这个地方的handle必须和上面的script.src最后一致。
function handle(data) {
// console.log(data);
var obj = JSON.parse(data);
console.log(obj);
var ul = document.createElement("ul");
ul.innerHTML += "<li>" + obj.title + "</li>";
ul.innerHTML += "<li>" + obj.user.name + "</li>";
ul.innerHTML += "<li>" + obj.user.email + "</li>";
document.body.appendChild(ul);
}
</script>
</body>
</html>
<?php
header('content-type:text/html;charset=utf-8');
// 因为js只支持utf-8,因此这个地方需要确定下,尽管默认也是utf-8。
http://php.io/test2.php?id=2&jsonp=handle
// id: 查询参数,这是可选。
$id = $_GET['id'];
// 前端的回调函数的名称[必选],
// 这个地方的jsonp也可以是其他名字,只要跟demo4中url最后一个参数名字保持一致即可。
// callback在下面最后再将一些内容传递回去,给到demo4.html中的handle去处理。
$callback = $_GET['jsonp'];
// 模拟接口数据,根据查询条件返回不同的值
// 这个数组有三个元素,每个元素是字符串,字符串的内容是json格式的数据
$users = [
0=>'{"name":"朱老师", "email":"peter@php.cn"}',
1=>'{"name":"西门老师", "email":"xm@php.cn"}',
2=>'{"name":"猪哥", "email":"pig@php.cn"}'
];
if (array_key_exists(($id-1), $users)) {
// 查询结果保存到变量$user中
$user = $users[$id-1];
}
// 返回前端给回调的参数是一个json格式的数据
$json = '{
"title": "用户信息表",
"user": ' . $user . '
}';
// 生成js函数的调用语句
echo $callback .' (' . json_encode($json) . ')';
5. classList对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>classList对象</title>
<style>
.red {
color: red;
}
.bgc {
background-color: yellow;
}
.blue {
color: green;
}
</style>
</head>
<body>
<p class="red">大家晚上好</p>
<h3>classList对象你会了吗?</h3>
<script>
// classList: 操作class中的内容
var h3 = document.querySelector("h3");
h3.className = "red";
// h3.className = "red bgc";
// 添加样式
// h3.classList.add("red");
// h3.classList.add("bgc");
// 移除样式
// h3.classList.remove("bgc");
// 切换样式
// h3.classList.toggle("red");
// 替换样式
h3.classList.replace("red", "blue");
</script>
</body>
</html>
6. 自定义数据属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>自定义数据属性</title>
</head>
<body>
<div
id="user"
data-id="110"
data-user-name="peter"
data-email="peter@php.cn"
>
用户信息
</div>
<script>
// dataset对象用于获取自定义数据属性的内容
// 自定义数据属性必须以data-为前缀
var user = document.getElementById("user");
// dataset获取data-的数据属性内容时,必须省略data-
console.log(user.dataset.id);
console.log(user.dataset.userName);
// 这个地方的userName,而不是上面的-user-name,这种格式要注意。
console.log(user.dataset.email);
</script>
</body>
</html>
7. 事件:事件监听与派发(addEventListener,dispatchEvent)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>事件</title>
</head>
<body>
<button>点击广告</button>
<script>
// 获取按钮
var btn = document.querySelector("button");
// 事件监听
btn.addEventListener("click", function () {
console.log("广告被点击了");
});
// 事件派发:每0.1秒点击一次
setInterval(function () {
var event = new Event("click");
btn.dispatchEvent(event);
}, 100);
</script>
</body>
</html>
8. 事件冒泡
- 一个元素的事件,其实也是发生在其父级上面的事件
- 因此可以不断向上“冒泡”
- 区分ev.target(事件对象)和ev.currentTarget(当前对象)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>事件冒泡</title>
</head>
<body>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<script>
// 给所有的li添加一个点击事件
var lis = document.querySelectorAll("li");
for (var i = 0; i < lis.length; i++) {
lis[i].addEventListener("click", function (ev) {
// ev.target: 触发事件的元素
// console.log(ev.target);
// ev.currentTarget: 绑定事件的元素
console.log(ev.currentTarget);
});
}
// 给li的父元素ul也添加点击事件
document.querySelector("ul").addEventListener("click", function (ev) {
// ev.target: 触发事件的元素
// console.log(ev.target);
// ev.currentTarget: 绑定事件的元素
console.log(ev.currentTarget);
});
// ul的父级body也添加点击事件
document.body.addEventListener("click", function (ev) {
// ev.target: 触发事件的元素
// console.log(ev.target);
// ev.currentTarget: 绑定事件的元素
console.log(ev.currentTarget);
});
// 根元素
document.documentElement.addEventListener("click", function (ev) {
// ev.target: 触发事件的元素
// console.log(ev.target);
// ev.currentTarget: 绑定事件的元素
console.log(ev.currentTarget);
});
</script>
</body>
</html>
9. 事件委托(代理)
- 基于的正是事件冒泡的原理,由父级直接来接收、代理子级的事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>事件委托/代理</title>
</head>
<body>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
<script>
//根据事件冒泡的原理,当前子元素的事件,必然会冒泡到父级上的同名事件上
// 此时,只需要给所有的li的父级ul添加点击事件就可以
document.querySelector("ul").addEventListener("click", function (ev) {
// ev.target: 触发事件的元素, <li>
console.log(ev.target);
// ev.currentTarget: 绑定事件的元素, <ul>
console.log(ev.currentTarget);
});
</script>
</body>
</html>
10. 作业 (这块在自己的pm中已经实现,具体见pm中的index.php)
- 写一个jsonp的实现案例,根据id获取商品信息
- 写一个事件代理的案例(自定义)