jQuery最详细的入门教程(原理+知识清单+案例)Ⅳ

298 阅读11分钟
很喜欢这句话:“任何事情都应该去尝试一下,因为你无法知道,什么样的事或者什么样的人将会改变你的一生。

第四期内容

  1. 类数组对象操作
  2. 添加自定义函数
  3. 封装自定义插件
  4. ajax
  5. *****跨域

一. 类数组对象操作:

  1. 问题: jQuery中所有查找结果都是类数组对象,但是js语言非常歧视类数组对象。虽然长的像数组,但是无法使用数组家好的函数!就会限制jQuery查找结果发挥更大的作用

  2. 解决: jQuery中模仿着数组家最常用的两个函数forEach和indexOf,专门定义了两个新函数each()和index(),专门供jQuery查找结果的类数组对象使用!

  3. 遍历查询结果中每个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>
运行结果:

  1. 获取元素在查找结果中的位置:模仿数组家的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>
运行结果: 

二. 添加自定义函数:

  1. 问题: 我常用的功能,jquery家没有提供

  2. 解决: 自定义一个函数,添加到jQuery家原型对象中

  3. 如何:

jQuery.prototype.自定义函数=function(形参列表){
		... ...
}
  1. 问题: 如何获得将来调用这个公共函数的.前的jQuery查找结果对象?

  2. 解决: this:

	因为将来: $(...).自定义函数()
	所以:           this->.前的jquery查找结果对象$(...)
	所以: jQuery原型对象方法中的this,不用再$(this)的。因为已经是jQuery子对象了。
  1. 示例: 为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>
运行结果:  

三. 封装自定义插件:

  1. 什么是插件/组件: 包含专属的HTML,CSS,JS的独立的可重用的页面功能区域

  2. 为什么: 重用!

  3. 何时: 如果页面中一个独立的功能区域在项目中多个位置被反复用到,都要封装为插件,再反复使用插件!

  4. 其实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>
运行结果:

  1. 模仿jqueryui风格,封装自定义jquery插件:

    (0). 前提: 已经用传统的HTML,css和js实现了插件的功能,只不过和别的网页内容混在一起,没有独立出来,无法重用而已!

     所以,封装插件的过程,其实只是一个提取HTML,CSS和js的过程。而不是从0开始制作一个功能!
    

    (1). 先创建一个独立的css文件,将原页面中插件相关的css剪切到独立的css文件中保存。

    (2). 再创建一个独立的js文件,在独立的js中向jquery原型对象中添加一个自定义插件函数,做2件事:

    a. 为要应用插件的元素及其子元素自动添加必须的class

    b. 自动绑定事件处理函数:

     因为原页面中已经实现了插件的功能,所以只要单纯将原页面中的事件绑定代码剪切到插件函数中,放在自动添加class之后即可!
    
  2. 如何在HTML页面中使用自定义jquery插件: 同使用jqueryui插件完全一样!

  3. 总结: 从此如果其他项目也想用这个插件:

    (1). 只要将包含插件的css和js的文件夹/压缩包拷贝到新项目

    (2). 在新项目的网页中引入jquery、插件的js和css

    (3). 调用一次插件函数即可!

  4. 示例: 封装一个手风琴插件

.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

  1. jquery中已经封装好了简化版的一句话发送ajax请求的函数

  2. 固定用法:

    $.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来获得服务器端返回的结果执行后续操作
     }
    

    })

  3. 示例: 使用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>
运行结果: 

五. *****跨域:

  1. 什么是: 一个网站下的网页中请求了别的网站的资源!

  2. 比如:

	<link rel="stylesheet" href="别的网站的css">
	<script src="别的网站的js">
	<img src="别的网站的图片">
	<a href="别的网站的网页">
	<iframe src="别的网站的网页">
  1. 包括:

     	        网页来源    请求   要请求的资源所在的网址
    

    (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地址
    
  2. 问题: 浏览器禁止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响应头
  1. 示例: 抛出跨域错误:
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数组中所有客户端网页的跨域请求。