很喜欢这句话:“任何事情都应该去尝试一下,因为你无法知道,什么样的事或者什么样的人将会改变你的一生。
第四期内容
- 类数组对象操作
- 添加自定义函数
- 封装自定义插件
- ajax
- *****跨域
一. 类数组对象操作:
-
问题: jQuery中所有查找结果都是类数组对象,但是js语言非常歧视类数组对象。虽然长的像数组,但是无法使用数组家好的函数!就会限制jQuery查找结果发挥更大的作用
-
解决: jQuery中模仿着数组家最常用的两个函数forEach和indexOf,专门定义了两个新函数each()和index(),专门供jQuery查找结果的类数组对象使用!
-
遍历查询结果中每个DOM元素: each():模仿数组家的forEach:
(1). 回顾: 数组家的forEach
1). arr.forEach(function(elem, i, arr){ ... }) 2). 依次遍历arr数组中每个元素,每遍历一个元素,就自动执行回调函数,对每个元素执行相同的操作(2). jq中: 新增each()
1). $(...).each(function(i, elem){ //i 获得当前正在遍历的下标位置 //elem 获得当前正在遍历的一个DOM元素 })(3). 示例: 遍历ul下每li,并按要求修改符合条件的li
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
</style>
</head>
<body>
<ul id="list">
<li>98</li>
<li>85</li>
<li>33</li>
<li>99</li>
<li>52</li>
</ul>
<script src="js/jquery-1.11.3.js"></script>
<script>
//回顾forEach
var arr=["亮亮","然然","东东"];
console.log(arr);
arr.forEach(function(elem){
console.log(`${elem} - 到!`)
})
//请给每个不足60分的成绩+10分,并将超过90分的成绩用绿色背景标识出来
console.log($("ul>li"));//爹是Object
//错误: 类数组对象不能用数组家的函数
//$("ul>li").forEach(function(elem){
//正确:
$("ul>li").each(function(i,elem){
//先将当前正在遍历的dom元素elem,转为jquery对象
var $elem=$(elem);
//再获得元素的内容,转为整数
var score=parseInt($elem.html());
// 副本 按值传递
//如果elem的内容<60,就+10分
if(score<60){
score+=10;//修改副本,页面是不变的
//必须将新值再放回去,页面才能改变
$elem.html(score);
}else if(score>=90){//否则如果elem的内容>=90,就让elem变绿
$elem.css("background-color","lightGreen")
}
})
</script>
</body>
</html>
运行结果:

-
获取元素在查找结果中的位置:模仿数组家的indexOf()
(1). 回顾数组家indexOf()
a. var i=arr.indexOf(要找的元素值)
b. 在数组arr中查找“要找的元素值”出现的下标位置。如果找不到返回-1
(2). jq中: 新增index()
a. var i=$(...).index(DOM元素)
b. 查找一个指定DOM元素在$(...)查找结果中的下标位置
(3). 示例: 五星评价

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
.score {
list-style: none;
margin: 0;
padding: 0;
}
.score li {
display: inline-block;
width: 50px;
height: 50px;
border: 1px solid #f00;
border-radius: 50%;
cursor: pointer;
}
</style>
</head>
<body>
<h3>请打分</h3>
<ul class="score">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<script src="js/jquery-1.11.3.js"></script>
<script>
//回顾indexOf
var arr=["亮亮","然然","东东"];
//想查找然然的位置
var i=arr.indexOf("然然");
console.log(`然然在${i}位置`);
//获得当前单击的li在所有li中的位置i,i及其执行的都变为红色,i之后的都变为白色
//DOM 4步
//1. 查找触发事件的元素
//本例中: ul下的所有li都能单击
$("ul>li")
//2. 绑定事件处理函数
.click(function(){
//3. 查找要修改的元素
//4. 修改元素
//先获得当前点击的li在所有li中的下标位置i
var i=$("ul>li").index(this)
//再将<i+1位置的所有li背景变为黄色
//错误: i是变量!直接放在字符串中,就不是变量了!
//$(`ul>li:lt(i+1)`)
//正确: 用模板字符串将变量i拼接到选择器中
$(`ul>li:lt(${i+1})`).css("background-color","yellow");
//再见>i位置的所有li背景变为白色
$(`ul>li:gt(${i})`).css("background-color","#fff");
})
</script>
</body>
</html>
运行结果:

二. 添加自定义函数:
-
问题: 我常用的功能,jquery家没有提供
-
解决: 自定义一个函数,添加到jQuery家原型对象中
-
如何:
jQuery.prototype.自定义函数=function(形参列表){
... ...
}
-
问题: 如何获得将来调用这个公共函数的.前的jQuery查找结果对象?
-
解决: this:
因为将来: $(...).自定义函数()
所以: this->.前的jquery查找结果对象$(...)
所以: jQuery原型对象方法中的this,不用再$(this)的。因为已经是jQuery子对象了。
- 示例: 为jQuery家添加对查找结果中所有DOM元素内容求和的sum()函数
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="js/jquery-1.11.3.js"></script>
<script>
//在jQuery家原型对象中添加共用的函数sum
jQuery.prototype.sum=function(){
console.log(`调用了一次我自定义的sum函数`)
//遍历sum.前的查找结果中的每个dom元素的内容,并累加求和
var result=0;
//问题: 如何获得将来调用这个公共函数的.前的查找结果对象?!
//将来: $("ul>li").sum()
// this->$("ul>li")
for(var i=0;i<this.length;i++){
//因为this是$("ul>li"),是类数组对象,其中包含多个DOM元素li对象
//this[i]取出的应该是一个DOM的li对象
result+=parseInt(this[i].innerHTML)
}
return result;
}
</script>
</head>
<body>
<ul>
<li>85</li>
<li>91</li>
<li>73</li>
<li>59</li>
</ul>
<script>
//比如: 我经常需要对找到的所有元素内容求和!
// jquery家子对象
var result=$("ul>li").sum()
console.log(result);
</script>
</body>
</html>
运行结果:

三. 封装自定义插件:
-
什么是插件/组件: 包含专属的HTML,CSS,JS的独立的可重用的页面功能区域
-
为什么: 重用!
-
何时: 如果页面中一个独立的功能区域在项目中多个位置被反复用到,都要封装为插件,再反复使用插件!
-
其实jquery官方提供了一套极好用的插件: jQuery UI
(1). jqueryui.com
(2). 如何使用: 仅手风琴效果举例:
a. 在页面中先引入jquery,再引入jqueryui的css和js
<script src="js/jquery-1.11.3.js">
<script src="js/jquery-ui.js">
<link rel="stylesheet" href="css/jquery-ui.css">
b. 按插件要求编写HTML内容(同bootstrap)
不同点: 使用jqueryUI时,HTML元素上不用加任何class!
c. 在自定义js中,用jquery找到插件的父元素,调用jqueryui提前定义好的插件函数即可!
(3). 问题: 只有pc端,没有移动端,且不支持响应式!——jQuery几乎退出历史舞台,被bootstrap替代了!
(4). 示例: 使用jqueryui插件实现手风琴效果
<!DOCTYPE html>
<html>
<head>
<title> new document </title>
<meta charset="utf-8">
<script src="js/jquery-1.11.3.js"></script>
<script src="js/jquery-ui.js"></script>
<link rel="stylesheet" href="css/jquery-ui.css">
</head>
<body>
<h1>jQueryUI:Widgets —— Accordion</h1>
<!--以下HTML从14_accordion.html中复制过来的,且去掉了所有的class-->
<div id="my-accordion">
<div>《西游记》简介</div>
<div>一个和尚和四个动物的故事: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Similique nulla voluptas velit minus esse voluptatem illum quis magni nihil sint facilis cupiditate nobis quia ab neque. Modi veniam omnis nisi? </div>
<div>《水浒传》简介</div>
<div>105个男人和三个女人的故事: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis provident sapiente aperiam reprehenderit repellat rem magnam vel odio quia harum hic impedit dolorem similique ea est consequatur adipisci at nemo!
</div>
<div>《红楼梦》简介</div>
<div>一个男人和一群女人的故事: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus minima quidem aspernatur eligendi optio cupiditate minus nam expedita? Aliquid veritatis doloribus maxime vel dicta illo unde iusto qui quasi doloremque.</div>
</div>
<script>
$("#my-accordion").accordion();
</script>
</body>
</html>
运行结果:

-
模仿jqueryui风格,封装自定义jquery插件:
(0). 前提: 已经用传统的HTML,css和js实现了插件的功能,只不过和别的网页内容混在一起,没有独立出来,无法重用而已!
所以,封装插件的过程,其实只是一个提取HTML,CSS和js的过程。而不是从0开始制作一个功能!(1). 先创建一个独立的css文件,将原页面中插件相关的css剪切到独立的css文件中保存。
(2). 再创建一个独立的js文件,在独立的js中向jquery原型对象中添加一个自定义插件函数,做2件事:
a. 为要应用插件的元素及其子元素自动添加必须的class
b. 自动绑定事件处理函数:
因为原页面中已经实现了插件的功能,所以只要单纯将原页面中的事件绑定代码剪切到插件函数中,放在自动添加class之后即可! -
如何在HTML页面中使用自定义jquery插件: 同使用jqueryui插件完全一样!
-
总结: 从此如果其他项目也想用这个插件:
(1). 只要将包含插件的css和js的文件夹/压缩包拷贝到新项目
(2). 在新项目的网页中引入jquery、插件的js和css
(3). 调用一次插件函数即可!
-
示例: 封装一个手风琴插件
.accordion{width:80%; margin:0 auto;}
.accordion>.title{
background:#eee; border:1px soild #aaa;
padding:6px; font-size:1.5em;
font-weight:bold; cursor:pointer;
}
.accordion>.content{
border-left:1px solid #eee;
border-right:1px solid #eee;
}
.accordion>:last-child{
border-bottom:1px solid #eee;
}
.fade{
height:0;
opacity:0;
overflow:hidden;
transition:all .5s linear;
}
.in{
height:84px;
opacity:1;
}
jQuery.prototype.myAccordion=function(){
console.log(`调用了自定义的myAccordion()函数`)
//1. 为要应用插件的当前元素及其子元素自动添加class
//问题: 如何获得当前要应用插件的元素?
//解决:this->将来要应用插件的.前的元素
//因为this->$("#my-accordion"),已经是jQuery子对象,所以不用$(this);
var $parent=this;//$parent->id为my-accordion的父元素div
//为父元素div本身添加class accordion
$parent.addClass("accordion")
//为父元素下所有偶数位置的元素加title class
.children(":even").addClass("title")
//return 所有偶数位置的元素
//为每个偶数位置的元素的下一个兄弟元素加content和fade class
.next().addClass("content fade")
//return 所有奇数位置的元素
//为所有奇数位置的元素中第一个元素加class in
.first().addClass("in");
//2. 自动绑定事件
$(".accordion").on("click",".title",e=>
$(e.target).next(".content").toggleClass("in")
.siblings(".content").removeClass("in")
);
}
//将来: 比如: id为my-accordion的div想应用手风琴插件
//按jquery ui做法:
//$("#my-accordion").myAccordion()
//找到插件父元素 调用自定义插件函数
<!DOCTYPE html>
<html>
<head>
<title> new document </title>
<meta charset="utf-8">
<script src="js/jquery-1.11.3.js"></script>
<script src="my-ui/my-ui.js"></script>
<link rel="stylesheet" href="my-ui/my-ui.css">
</head>
<body>
<h1>使用“高度动画”实现“手风琴”组件</h1>
<!--按插件规定编写HTML内容,一个class都不要加!-->
<div id="my-accordion">
<div>《西游记》简介</div>
<div>一个和尚和四个动物的故事: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Similique nulla voluptas velit minus esse voluptatem illum quis magni nihil sint facilis cupiditate nobis quia ab neque. Modi veniam omnis nisi? </div>
<div>《水浒传》简介</div>
<div>105个男人和三个女人的故事: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Omnis provident sapiente aperiam reprehenderit repellat rem magnam vel odio quia harum hic impedit dolorem similique ea est consequatur adipisci at nemo!</div>
<div>《红楼梦》简介</div>
<div>一个男人和一群女人的故事: Lorem ipsum dolor sit amet, consectetur adipisicing elit. Delectus minima quidem aspernatur eligendi optio cupiditate minus nam expedita? Aliquid veritatis doloribus maxime vel dicta illo unde iusto qui quasi doloremque.</div>
</div>
<script>
//查找要应用插件的父元素,只需要调用自定义的插件函数
$("#my-accordion").myAccordion();
//2件事:
//1. 自动添加class
//2. 自动绑定事件
</script>
</body>
</html>
运行结果:

四. ajax
-
jquery中已经封装好了简化版的一句话发送ajax请求的函数
-
固定用法:
$.ajax({
url:"服务器端接口地址", type:"get或post", data:{ 变量: 值, 变量: 值 }, //发送到服务器端的参数变量和值 dataType:"json", //因为几乎所有服务器端返回的结果都是json字符串,都必须反复调用JSON.parse()将json字符串,转为js中的对象和数组,程序才能用。所以,发送ajax请求时,只要执行dataType参数值为json,$.ajax就会自动调用JSON.parse()将返回的json字符串,转化为对象或数组 . . 延迟 . //相当于onreadystatechange=function(){ ... } success: function(result){ //只要请求响应成功,成功获得服务器端返回的结果时,自动调用success回调函数。并自动将服务器端返回的结果,经过JSON.parse()后翻译为对象,交给result变量。 //在success函数中,就可使用result来获得服务器端返回的结果执行后续操作 }})
-
示例: 使用ajax函数发送多种请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="js/jquery-1.11.3.js"></script>
</head>
<body>
<script>
//想从东哥新浪云服务器的/index接口获得学子商城首页4个商品
//服务器端接口地址: http://xzserver.applinzi.com/index
$.ajax({
url:"http://xzserver.applinzi.com/index",
type:"get",
dataType:"json",
success:function(result){
console.log(result);
}
});
//想从东哥新浪云服务器的/details接口获得5号商品的详细信息
//服务器端接口地址:
// http://xzserver.applinzi.com/details
// 参数: lid=商品编号
$.ajax({
url:"http://xzserver.applinzi.com/details",
type:"get",
data:{ lid:5 },
dataType:"json",
success:function(result){
console.log(result);
}
});
//想利用东哥新浪云服务器的/signin接口登录学子商城
//服务器端接口地址:
//http://xzserver.applinzi.com/users/signin
//post类型:
//参数: uname=用户名&upwd=密码
// dingding 123456
$.ajax({
url:"http://xzserver.applinzi.com/users/signin",
type:"post",
data:{ uname:"dingding", upwd:"123456" },
dataType:"json",
success:function(result){
//result两种情况:
//如果用户名密码正确: {ok:1, uname:"dingding"}
//如果用户名密码错误: {ok:0, msg:"用户名或密码不正确"}
console.log(result);
if(result.ok==1){
document.write(`<h1>Welcome ${result.uname}</h1>`)
}else{
document.write(`<h1 style="color:red">${result.msg}</h1>`)
}
}
})
</script>
</body>
</html>
运行结果:

五. *****跨域:
-
什么是: 一个网站下的网页中请求了别的网站的资源!
-
比如:
<link rel="stylesheet" href="别的网站的css">
<script src="别的网站的js">
<img src="别的网站的图片">
<a href="别的网站的网页">
<iframe src="别的网站的网页">
-
包括:
网页来源 请求 要请求的资源所在的网址(1). 域名不同: www.a.com -> www.b.com
(2). 子域名不同: oa.tedu.cn -> hr.tedu.cn
(3). 端口号不同: localhost:5500 -> localhost:3000
(4). 协议不同: 12306.cn -> 12306.cn
(5). 同一台主机: http://localhost -> http://127.0.0.1
域名 ip地址 -
问题: 浏览器禁止ajax发送跨域请求!
报错: Access to XMLHttpRequest at 'http://localhost:3000/' from origin
'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
翻译: 从源头'http://127.0.0.1:5500'到目标'http://localhost:3000/'的xhr的访问被CORS策略阻止了。因为响应回来的资源中没有设置Access-Control-Allow-Origin响应头
- 示例: 抛出跨域错误:
const http=require("http");
http.createServer(function(req,res){
var weather=`晴 21~31度`;
res.writeHead(200,{
"Content-Type":"text/plain;charset=utf-8"
});
res.write(weather);
res.end();
}).listen(3000)
如何运行
1. 右键单击16_server.js,选择"在终端中打开"或"在命令行中打开"
2. 等待打开的窗口中显示 路径> 输入node 16_server.js 回车 启动服务器端
3. 打开浏览器,地址栏输入http://localhost:3000
看到晴 21~31度,说明服务器端启动成功!
如果启动服务器报: listen xxxxx :: 3000错误
原因: 同时开了多个命令行窗口,都监听3000端口,发生端口冲突
解决: 连续点命令行窗口右上角垃圾桶图标,关闭所有命令行窗口
然后再从第一步开始运行。始终保持命令行窗口只开一个

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="js/jquery-1.11.3.js"></script>
<script>
$.ajax({
url:"http://localhost:3000",
type:"get",
success:function(result){
document.write(`<h1>今日北京: ${result}</h1>`)
}
})
</script>
</body>
</html>
运行结果:

6. 跨域错误的原因: 浏览器都有一个同源策略
(1). 什么是同源策略:
当网页中发送ajax请求时,浏览器会检查ajax获得的响应结果和当前网页是否来自于同一个域名下的服务器。
a. 如果浏览器发现ajax获得响应结果和当前网页不是来自于同一个域名下,则不允许网页使用获得的返回结果
b. 只有浏览器发现ajax获得响应结果和当前网页来自于同一个域名下,才允许网页使用ajax返回的结果!
(2). 比如:

(3). 同源策略: Cross Origin Resources Sharing
跨 不同源头 资源 共享
只有网页来源和响应结果来源相同时才允许使用响应结果
7. 解决: 骗
修改服务器端代码的响应头中的Access-Control-Allow-Origin属性为客户端网页所在地址

8. 示例: 修改服务器端允许http://127.0.0.1:5500的网页跨域使用服务器端数据
const http=require("http");
http.createServer(function(req,res){
var weather=`晴 21~31度`;
res.writeHead(200,{
"Content-Type":"text/plain;charset=utf-8",
"Access-Control-Allow-Origin":"http://127.0.0.1:5500"
});
res.write(weather);
res.end();
}).listen(3000)
运行结果:
重新运行服务器端
重新用live server打开16_client.html
看到天气预报,说明成功!
今日北京: 晴 21~31度
9. 问题: 一个服务器端同时包含很多个接口程序,如果每个接口程序都要重复写这么长一坨代码,才能跨域——极其不便于维护?
10. 解决: 各种服务器端语言nodejs,java,...都提供专门支持跨域的中间件程序,仅以nodejs举例:
(1). 服务器端项目安装cors模块: npm i -save cors
结果: 服务器端项目/node_modules/cors文件夹
(2). 在服务器端项目主程序app.js中,先引入cors模块,再配置cors中间件
const cors=require("cors");
在app = express();之后
配置cors中间件
app.use(cors({
origin:['http://localhost:8080', "http://127.0.0.1:5500",... ...],
VUE脚手架 live server
//希望跨域的客户端网页地址
}))
(3). 结果: 凡是这台服务器的所有接口都可支持origin数组中所有客户端网页的跨域请求。
