1. 什么是jQuery
jQuery是第三方开发的执行DOM操作的极简化的函数库
第三方: 下载
执行DOM操作: jQuery还是在执行DOM操作
学习jQuery还是在学习DOM
五件事: 增删改查 事件绑定
极简化: jQuery是对DOM中每步操作都进行了终极的简化。但是,做事儿的步骤,并没有简化!
函数库: jQuery中一切都是用函数来解决问题,没有属性。
为什么: 2个原因:
1. 简单!
2. 解决了大部分浏览器兼容性问题:
凡是jQuery让用的,都没有兼容性问题
何时: 今后PC端的项目,以及主流的框架底层,都是用jQuery实现的。
趋势: jQuery仅适用于PC端,不适用于移动端
2. 如何使用jQuery:
1. 下载: 官网: jquery.com
版本: 1.x 唯一支持旧浏览器的版本
未压缩版:
优点: 拥有完备的注释,代码格式以及见名知意的变量名——学习和开发时
缺点: 体积大,不利于下载和传输
压缩版:
优点: 体积小,便于快速下载和传输
——生产环境
缺点: 去掉所有注释和代码格式,并极简化了变量名。
2.x 不再支持旧浏览器
3.x 不再支持旧浏览器,而且添加了部分新功能
2. 引入:
哪个页面需要用jQuery的简化版函数,都要先引入jQuery.js,再编写自定义的js代码
<script src="js/jquery-1.11.3.js">
<script src="js/自定义.js">
出于性能的考虑: 所有js代码的引入,都要放在<body>结尾。避免影响网页内容的加载
3. jQuery的原理:
当引入jquery.js时:
其实是在window中添加了一种新类型jQuery
jQuery类型也由两部分组成:
1. 构造函数function jQuery(){}
负责创建该类型的子对象
2. 原型对象jQuery.prototype
负责保存该类型所有子对象共有的函数
规定:
为了区分jQuery对象和DOM元素对象,今后,凡是保存jQuery对象的变量,习惯上都用:$xxx开头
比如: var $btn1=$("#btn1")
今后,凡是看到$开头的变量,都说明是一个jQuery子对象,都可调用jQuery家的简化版函数。
反之,如果一个变量没有以$xxx开头,很可能说明它不是jQuery类型的子对象,就不能调用jQuery加的函数。
结果: 调用$()等效于调用jQuery()等效于new jQuery()
对整个jQuery子对象调用简化版函数
jQuery子对象会自动将简化版函数翻译为DOM原生的函数,作用于内部保存的DOM元素上
比如: 想为jq对象$btn1中的按钮绑定单击事件:
$btn1.click(function(){ ... })
等效于: $btn1对象中的btn元素.addEventListener("click",function(){ ... })
结果: 事件处理函数function(){ ... }其实,还是直接绑定在DOM元素按钮上的。
事件触发时,调用事件处理函数的.前的主语还是DOM按钮对象。
所以,事件处理函数中的this和e.target,得到的都是DOM按钮对象。
如果想对this或e.target继续调用简化版函数,都应该变为$(this)或$(e.target)。
也就是新创建一个小的jQuery子对象,将this或e.target包装起来,才可继续调用jQuery加的简化版函数
3. 查找元素:
按选择器查找元素: $("选择器")
jQuery不但支持所有CSS选择器,而且还增加了一批新增的jQuery专属的选择器:
包括:
回顾: CSS中的子元素过滤:
:first-child 作为其父元素下第一个子元素的所有元素
:last-child
:nth-child(i)
:only-child
总结: 根据元素在其父元素内的相对下标位置选择元素。且序号从1开始
何时: 如果按照元素在其父元素内的相对位置选择元素时
基本过滤: jQuery新增:
:first :last
:eq(i) :lt(i) :gt(i)
:even :odd
偶数 奇数
特点: 先将符合条件的元素取出,统一放入一个集合中。按元素在集合中的编号选择元素。且下标从0开始。
何时: 今后希望打破父元素的界限,对所有子元素统一编号,按位置查找元素时
总结: 今后只要能用css做的效果,一定首选css做,因为css的效率就是比js高!且css没有框架和平台的兼容性问题。
即使jquery中的选择器,也必须首选css选择器,css选择器做不了的,才被迫选择jquery新增的选择器。因为jQuery新增的选择器都是用js程序在底层模拟的——效率低的!
jQuery简化版函数的特点:
**1. 一个函数两用:**
问题: jQuery中没有属性,只有函数:
比如: 获得按钮的内容:
DOM: btn.innerHTML
修改按钮的内容:
DOM: btn.innerHTML=新值
但是在jQuery中都是函数,没有属性,怎么实现对一个属性的获取或添加操作!
解决: 重载: 同一个函数,根据传入参数的不同,执行不同的操作。
jQuery中: 一个修改属性的函数,如果没给新值,就默认执行获取属性值的的操作。如果给了新值参数,就变成执行修改属性值的操作。
比如: 获得按钮的内容:
jQuery中: var 内容=$btn.html() ——获取
修改按钮的内容:
jQuery中: $btn.html(新值) ——修改
总结: jquery中凡是修改属性或内容的函数,都是一个函数两用。如果没给新值,默认执行出去属性值或内容的操作。如果给了新值,就变成修改属性值或内容的操作。
2. 自带遍历:
对整个jQuery类数组对象,调用一次简化版函数,等效于对类数组对象内部保存的所有DOM元素,
都调用一次函数。 ——jQuery对象自动的功能。
总结: $的原理:
1. 创建jQuery对象,封装DOM元素: 2种情况:
1. 先查找再封装: $("选择器")
2. 直接封装: $(DOM元素) 经常: $(this) 或 $(e.target)
1. 查找:
回顾属性选择器: CSS中已有
[属性名] 选择包含指定属性的元素
比如: [title] 选择包含title属性的元素
[属性名=值] 选择包含指定属性,且属性值为指定值的元素
比如: [title=help] 选择包含title属性,且title属性值为help的元素
[属性名^=值] 选择包含指定属性,且属性值以指定值开头的元素。
比如: [title^=he] 选择包含title属性,且title属性值以he开头的元素
[属性名$=值] 选择包含指定属性,且属性值以指定值结尾的元素。
比如: [title$=st] 选择包含title属性,且title属性值以st结尾的元素
[属性名*=值] 选择包含指定属性,且属性值中包含指定内容的元素。
比如: [title*=es] 选择包含title属性,且title属性值中包含es的元素
[属性名!=值] jQuery新增的
选择不包含指定属性的元素,以及虽然包含指定属性但属性值不等于指定值的元素。
等效于: :not([属性名=值])
比如: [title!=help] 可选择两种:
1. 选择不包含title属性的元素
2. 选择虽然包含title属性,但title属性值不是help的元素
内容过滤: jQuery新增
什么是: 用元素的内容文本作为查找条件
何时: 当元素的class或其他选择器无法区分这个元素时,就可以考虑用元素的内容作为判断条件
如何:
1. :contains(文本) 选择元素内容中包含指定文本的元素
2. :has(选择器) 选择子元素中包含符合条件的元素的父元素
可见性过滤: jQuery新增
1. :visible 选择所有可见的元素
2. :hidden 选择所有隐藏的元素
只能选择display:none的和<input type="hidden"
表单元素过滤选择器: jQuery新增
什么是: 以表单元素的类型来选择指定的表单元素
何时: 今后,如果按类型选择表单元素时
包括:
:input 选择所有表单元素: input select textarea button
vs input 只选择input元素
:类型名 每种type值都对应着一个专门的选择器,比如: :text :password :radio :checkbox ...
2. 修改: 以下所有函数都是一个函数两用
说明: 技术文档/手册中的习惯:
如果一个参数可写可不写,通常都会用[]括起来,表示可选: 比如: $元素.html(["新内容"]),表示参数"新内容"可写可不写,今后实际调用时,千万不用加[]。[]只是技术手册中的一种书写习惯而已。
内容:
1. 获取或修改原始HTML片段:
DOM: 元素.innerHTML
jq中: $元素.html([新内容])
2. 获取或修改纯文本内容:
DOM: 元素.textContent
jq中: $元素.text([新内容])
3. 获取或修改表单元素的值:
DOM: 元素.value
jq中: $元素.val([新内容])
属性:
1. HTML标准属性:
DOM: 即可用getAttribute()和setAttribute()
又可用.属性名
jq中: $元素.attr("属性名"[,"新属性值"])
代替getAttribute()和setAttribute()
$元素.prop("属性名"[,"新属性值"])
代替了DOM中的.操作!
2. 状态属性: 值都是bool类型
DOM: 不能用getAttribute()/setAttribute()操作
只能用 .状态名 访问
jq中: 也不能用$元素.attr()
只能用$元素.prop("状态属性",bool)
比如: 禁用元素:
$元素.prop("disabled",true)
比如: 选中一个checkbox
$元素.prop("checked",true)
3. 自定义扩展属性:
DOM: 只能用getAttribute()和setAttribute
不能用 .属性名 访问
jq中: 只能用$元素.attr()
不能用$元素.prop()
样式:
1. 获取或修改内联样式:
DOM: 元素.style.css属性="值"
jq中: $元素.css("css属性","值")
简写: 控制显示隐藏:
$元素.show() 等效于 .css("display","block")
.hide() 等效于 .css("display","none")
问题: DOM中获取样式不能用.style,因为.style只能获得内联样式。所以,DOM中被迫使用getComputedStyle()来获得计算后的样式。jQuery中,.css()函数有没有这个问题?
答: 没有!jq中.css()可自动切换.style和getComputedStyle()。使用.css()即可以修改所有样式,也可以获取所有样式。为什么:
.css()内部自动判断:
如果没有给新的css属性值,就调用getComputedStyle()自动执行获取操作。
如果给了新的css属性值,就自动切换成.style.css属性,执行修改操作。
问题: 用.style.css属性一次只能修改一个css属性,如果同时批量修改多个css属性,代码会很繁琐!
解决: 今后只要批量修改css属性,都要用class
问题: DOM中.className只能整体替换所有class中的类名,或者使用字符串操作,
修改或删除其中某个一类名。比如: <button class="btn btn-success active">,
想去掉btn-success,保留btn和active,只能用字符串替换操作,非常不方便。
解决:
2. 专门操作class的函数:
$元素.addClass("类名 ...")
$元素.removeClass("类名")
var bool=$元素.hasClass("类名") 判断一个元素是否包含某个class
简化: 因为经常需要在有和没有某个class之间来回切换元素的样式,所以,今后,只要反复切换一个元素的class时,可简写为: $元素.toggleClass("类名")
原理: 等效于:
if(有)
就移除
else
就添加
强调: toggleClass()不能代替addClass()
toggleClass()是一次有,一次没有
addClass()始终添加
只有明确在两种状态之间来回切换时,才能使用toggleClass()
总结: 所有修改属性和修改样式的方法:
.attr() .prop() .css() 其实都可以同时修改多个属性的值,只不过参数要用对象语法,保存多个属性值
比如: $img.attr({ alt:i, src:`img/${i}.jpg` })
$img.css({ top:100, left:100 })
3. 按节点间关系查找:
何时: 如果已经获得一个元素,找周围附近的元素时
1. 父子关系:
DOM: 元素.parentNode
元素.children
元素.firstElementChild
元素.lastElementChild
jq中: $元素.parent();
$元素.children(["选择器"]);
问题: children()只能查找直接子元素,无法在所有后代中查找
解决: $元素.find("选择器") 可在所有后代中查找符合条件的元素。
强调: 要用.find()必须写选择器
$元素.children(":first-child")
$元素.children(":last-child")
2. 兄弟关系:
DOM: 元素.nextElementSibling
元素.previousElementSibling
jq中: $元素.next() 之后一个兄弟元素
$元素.nextAll(["选择器"]) 之后所有兄弟元素
$元素.prev() 之前一个兄弟元素
$元素.prevAll(["选择器"]) 之前所有兄弟元素
$元素.siblings(["选择器"]) 除当前元素外,所有平级的兄弟元素(不分前后)
4. 添加/删除/替换/克隆
1. 添加新元素:
DOM: 3步:
1. 创建空元素
2. 设置关键属性
3. 将新元素添加到DOM树
1. 在指定父元素下所有子元素末尾追加
父元素.appendChild(新元素)
2. 在指定父元素下插入到某个现有子元素之前
父元素.insertBefore(新元素, 现有元素)
3. 替换指定父元素下的一个现有元素
父元素.replaceChild(新元素, 现有元素)
jq中: 2步:
1. 用$(`HTML片段`)就可创建片段中所有元素
比如: 创建a元素
var $a=$(`<a href="http://tmooc.cn">go to tmooc</a>`)
2. 依然需要将新元素添加到DOM树上
jq中: $父元素.append($新元素) 末尾追加
$父元素.prepend($新元素) 开头插入
$现有元素.before($新元素) 之前插入
$现有元素.after($新元素) 之后插入
$现有元素.replaceWith($新元素)
其实以上五个函数都有一个功能完全相同的兄弟函数:
$新元素.appendTo(父元素) 末尾追加
$新元素.prependTo($父元素) 开头插入
$新元素.insertBefore($现有元素) 之前插入
$新元素.insertAfter($现有元素) 之后插入
$新元素.replaceAll($现有元素)
为什么: 为了便于不同情况下的链式操作!
前五个: 以新元素作为参数的,返回的是父元素和现有元素,后续如果使用链式操作,只能对父元素或现有元素执行操作。
后五个: 统一都以新元素作为主语,返回的都是新元素,就可以用链式操作继续对新元素执行后续操作
何时:
如果后续操作时对父元素或现有元素的,就首选前五个
如果后续操作是对新元素的则首选后五个
2. 删除元素:
DOM: 父元素.removeChild(子元素)
jq中: $任意元素.remove();
补充: .is()
什么是: 专门判断一个元素是否符合条件
何时: 向判断一个元素是否符合指定的条件时
如何:
var bool=$元素.is("选择器")
如果$元素符合"选择器"条件要求,则返回ture
如果$元素不符合"选择器"条件要求,则返回false
总结: jquery函数的三大特点:
1. 自带遍历:
2. 凡是涉及修改的函数,都是一个函数两用
3. 多数函数都会返回正在操作的jq对象主语:
为了便于链式操作:
下一个函数的调用可以直接接在上一个调用函数之后。
链式操作的前提: 前一个函数返回的值必须刚好是下一个函数所需的主语!
jq中,如果对同一个元素,先后执行多个操作:
$元素.函数1().函数2().函数3()
因为每个函数都会返回.前的主语"$元素"
比如: 想为按钮绑定两个事件,同时修改样式:
$btn
.css("color","red")
//return $btn
.click(function(){ ... })
//return $btn
.blur(function(){ ... })
总结: 事件名列表:
blur 失去焦点
change 选中项改变
click 单击
dblclick 双击
focus 获得焦点
input 文本框中每输入一个文字
keydown 键盘按下
keyup 键盘抬起
mousedown 鼠标按键按下
mouseenter 鼠标进入 jq中专用
mouseleave 鼠标移出 jq中专用
mousemove 鼠标移动
mouseout 鼠标移出,DOM中使用
mouseover 鼠标移入,DOM中使用
mouseup 鼠标按键抬起
resize 窗口大小改变
scroll 页面滚动
1. 修改:
克隆: 复制一个和原DOM元素完全相同的一个新元素
如何: var $新元素=$现有元素.clone();
2. 事件绑定:
DOM中: 灵活的事件绑定:
元素.addEventListener("事件名",处理函数)
元素.removeEventListener("事件名",处理函数名)
jq中: jq中共有几种事件绑定的方式:
1. bind/unbind:
单纯代替addEventListener()和removeEventListener(); 用法完全相同!
2. 事件委托:
DOM: 事件委托3步:
1. 事件绑定在父元素上一次
2. 用e.target代替this
3. 判断e.target是否是想要的
jq中: .delegate()/.undelegate()
1. 事件还是绑定在父元素上一次
2. this又回来了!this->e.target
3. 不用自己写if($target.is("选择器"))
而是将选择器作为参数交给delegate(),请delegate自动判断当前元素是否符合要求
如何: $父元素.delegate("选择器条件","事件名",function(){ ... $(this) ...})
3. .on/.off():其实就是bind和delegate的封装重载
重载方式:
1. 如果直接给子元素绑定事件:
$元素.on("事件名",处理函数)
等效于bind(),等效于addEventListener()
2. 如果利用事件委托时:
$父元素
.on("事件名","选择器条件",function(){
$(this)
})
等效于delegate()
4. .事件名(): 仅等效于.bind(),无法利用事件委托的优势。
比如: .click() .change() .blur() ...
强调: 只有21种常用的事件被简写了,除这21种之外的,都不能用简写:
比如: input事件: 当文本框中输入一个文字后自动触发。
$txt.on("input",function(){ ... })
页面加载后自动执行:
通常js代码(比如: 查找元素, 事件绑定等),都必须在页面内容加载完成后,才能执行!
如何: 3种:
1. 将js代码放在<body>元素内容中的底部
缺点: 将来.html中引入js文件时,不一定都会把js文件在底部引入,很可能在<head>中引入。如果在<head>中引入,因为网页是顺序加载,所以执行先引入的js时,还没有网页内容呢,所以什么元素都找不到,什么事件也绑定不了
2. 通过绑定窗口的onload事件:
window有一个onload事件
事件会在所有网页内容(HTML,CSS,JS,图片)都加载完之后,才自动触发
即使在网页内容加载前提前绑定了这个事件,也不会提前触发!
如何:
1. window.onload=function(){
... ...
}
问题: 如果一个页面中引入了多个js文件,而每个js文件中都有window.onload=function(){}时,结果会覆盖!
解决:
window.addEventListener(
"load",
function(){
... ...
}
)
jq中: $(window).load(function(){
... ...
})
.load属于21种简写之一:
相当于.on("load",...)
相当于.bind("load")
相当于.addEventListener()
所以,不用担心覆盖问题
问题: onload事件需要等待所有网页内容(HTML, CSS, JS, 图片)全部加载完,才能执行!有点晚!
有些功能和样式和图片无关,就不应该强迫等待CSS和图片加载完才能用,应该一看到,就可使用!
解决:
3. 在onload之前还有一个DOMContentLoaded事件
DOMContentLoaded事件仅等待HTML和JS加载完就自动触发。不必等待CSS和图片——比onload早的多!
如何: DOMContentLoaded有兼容性问题:
//当网页内容 准备就绪后 自动执行
jq中: $(document).ready(function(){
})
问题: 绝大多数js初始化代码都要放在$(document).ready()中,代码太长
解决: $().ready(function(){ ... })
$(function(){ ... })
结论: 今后几乎所有jq代码都要放在$(function(){ ... })内。可以代替在js中的规矩: 所有代码都要放在匿名函数自调中。也就说,写了$(function(){ ... })就不用写匿名函数自调了。
总结:
1. 大部分和css和图片无关的初始化操作,比如: 事件绑定,ajax等,应该首先在DOMContentLoaded中提前执行!
2. 只有个别依赖于CSS和图片的初始化操作才被迫放在window.onlaod中
鼠标事件:
DOM: .mouseover 鼠标进入
.mouseout 鼠标移出
问题: 频繁进出子元素,也会冒泡触发父元素上的事件,容易引起误会。
jq中: .mouseenter和mouseleave代替mouseover和mouseout
简写: 因为经常需要同时绑定mouseenter和mouseleave一对儿事件,所以如果成对儿绑定鼠标进入和鼠标移出事件时,可简写为:
.hover(
function(){ ... },
function(){ ... }
)
强调: 通常情况下,绑定.hover,需要给.hover两个函数。因为绑定.hover等效于同时绑定两个事件mouseenter和mouseleave,自然需要两个处理函数。第一个处理函数绑定为mouseenter,第二个处理函数绑定为mouseleave
更简化: 如果刚好可以通过jquery的toggleClass将两个函数改为完全相同的两个函数,则也可以只给一个函数
.hover(
function(){ ... }
)
其中,这个函数,同时即给mouseenter,有绑给mouseleave。
模拟触发:
什么是: 虽然没有点在按钮上,也可以触发按钮的事件处理函数
如何: $元素.trigger("事件名")
触发
即使没有点在指定的元素上,也能触发该元素上绑定的事件处理函数
简写: 如果要触发的事件,刚好是21种简写中的一种,可使用事件处理函数一个函数两用的方式触发:
$元素.事件名()
强调: ()中不要加处理函数
3. 动画:
1. 简单动画:
写死的固定不变的三种动画效果:
1. 显示隐藏: .show() .hide() .toggle()
问题: .show() .hide()不加参数时,默认使用display:block和display:none控制瞬间显示隐藏
如果想有动画效果,必须加持续时间参数
2. 上滑下滑: .slideUp() .slideDown() .slideToggle()
3. 淡入淡出: .fadeIn() .fadeOut() .fadeToggle()
简单动画所有函数的共性问题:
1. 效果是写死的!几乎不可维护!
2. 用js定时器和DOM操作模拟的动画效果——效率极低
但是: .show() .hide() .toggle()在不加参数时,代替的是.css("display","block/none"),这个是没有效率和可维护性损失的。所以,这三个不加参数的情况,还是非常推荐使用的。
2. 万能动画:
什么是: 可对任意css属性应用动画效果
何时: 只要希望对任意css属性应用动画效果时
如何: $元素.animate({
css属性: 目标值
},动画持续时间)
让当前元素从现在的状态,经过指定的时间后,缓慢过渡到目标状态。
强调: animate()中只需要写目标值,animate()可自动获得当前的对应属性值,自动计算变化的距离。
问题:
1. 也是用js定时器和DOM操作模拟的动画效果
2. 其实只支持单个数值的css属性,不支持CSS3动画,不支持颜色过渡…
排队和并发:
1. 并发执行: 多个css属性同时变化
如何: 一个animate()内的多个css属性默认并发执行。
2. 排队执行: 多个css属性按顺序依次先后变化
如何: 对同一个元素先后调用多次animate()函数,多次animate函数中的css属性是排队执行
原理:
其实在jq中,每个元素都有一个动画队列。
调用animate时,其实不是立刻播放动画的意思,仅仅是将动画加入队列中保存
能否立刻执行动画,取决于当前动画之前是否有等待的后正在执行的其它动画。
在动画结束后自动执行:
问题: jq的动画函数都是用js定时器模拟的,所以都是异步的!
解决: 每个动画函数都提供了最后一个参数,是一个回调函数。写在这个回调函数中的代码,都是在动画结束后,才自动调用的。
停止动画: $元素.stop()
坑: 默认只能停止当前正在播放的一个动画。如果队列中后续还有其他动画,则依然继续执行。
解决: $元素.stop(true)
停止当前动画,且清除动画队列
判断一个元素是否在播放jquery动画:
:animated 选择器
专门匹配正在播放动画的元素
4. 类数组对象操作:
js中类数组对象很受歧视,长得像数组,但数组家的好用的函数,类数组对象都用不了。
但是,jq中创建的jq对象,都是类数组对象,于是jQuery就为jq对象,模拟出了两个和数组家功能相似的函数:
1. 遍历:
js数组中: arr.forEach(function(elem, i , arr){ ... })
jq中: $(查找结果).each(function(i, DOM元素){ ... })
依次取出.前的查找结果集合中的每个DOM元素,执行相同的操作!
2. 查找:
js数组中: var i=arr.indexOf(值)
jq中: var i=$(查找结果).index(元素)
查找元素在结果集合中的位置i
总结: $的原理:
1. 创建jq对象并查找元素: $("选择器")
2. 创建jq对象直接封装DOM元素: $(this) $(e.target)
3. 创建新的DOM元素: $(`html片段`)
4. 绑定DOM内容加载后就提前执行的事件: $(funciton(){})
1. 添加自定义函数:
何时: 当项目中经常需要用的一个功能,但是jQuery没有提供简化版函数
如何: 其实就是定义一个函数,保存在jQuery类型的原型对象中。
强调: jQuery原型对象中的方法中,可用this获得将来调用该函数的.前的jq对象。且不用再用$()封装this,因为this已经是jQuery子对象了:
简写:
其实jQuery.fn=jQuery.prototype
所以jQuery.prototype.sum=function(){ ... }
可简写为: jQuery.fn.sum=function(){ ... }
2. 封装自定义jQuery插件:
什么是: 拥有独立的HTML,css,js甚至是数据的可重用页面独立区域。
为什么: 代码重用和简化开发
何时: 只要在项目中发现反复使用的页面功能区域,都要封装为插件,再反复使用插件。
jQuery官方插件: jQuery UI
1. 去官方jqueryui.com下载jQueryUI库
2. 引入jquery-ui.css
引入jquery.js
引入jquery-ui.js
3. 按插件要求编写HTML代码,一个class都不用加!
4. 在自定义<script>中,找到插件的父元素,对父元素调用插件函数
原理:
侵入性: 不需要开发人员干预,插件函数可自动根据自身需要添加class和自定义扩展属性
优点: 省事!
缺点: 可维护性差
如何:
0. 前提: 插件的内容,样式和功能应该已经在页面中实现了!只不过,暂时和别的内容混在一起,没有提取出来而已!
1. 封装插件: 其实是一个提取的过程
1. 提取插件的css到一个独立的css文件中
鄙视: 如何避免组件间样式冲突:
css中属于一个组件内的选择器,都要统一以这个组件的父级class名开头,写完整路径。
2. 定义独立的js文件:
向jQuery类型的原型对象中添加一个插件函数:
2件事:
1. 自动侵入class
2. 自动绑定事件:只需要将原来的js代码剪切到插件函数中,换一下主语
2. 使用插件:
1. 引入插件.css
2. 引入jquery.js
引入插件.js
3. 按插件要求编写HTML内容,不用加class
4. 在自定义<script>中,找到插件的父元素,为其调用插件函数
3. ajax
jQuery中提供了一个已经封装好的可直接使用的ajax函数。
$.ajax({
url: "服务端接口地址"
type:"get或post",
data:{ 参数1: 值1, 参数2: 值2, ...},
dataType:"json", //请ajax函数帮忙自动将json字符串转换为js对象或数组,可直接使用。(自动调用JSON.parse())
success:function(result){ //回调函数: 在请求成功并成功收到响应后,自动执行!
//result 自动获得服务端返回的结果
}
})
//只有jQuery 3.x开始,才支持promise的.then()
不用自己封装的ajax了。
4. ***跨域:
什么是: 一个网站下的网页,请求/使用了另一个网站下的资源
比如:
1. <link>可引入别的网站的css文件
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
2. <script>可引入别的网站的js文件
<script src="https://code.jquery.com/jquery-1.12.4.min.js">
3. <img>可引入别的网站的图片
<img src="http: //www.baidu.com/img/baidu_jgylogo3.gif">
问题: ajax不允许跨域请求别的网站的数据
报错: Access to XMLHttpRequest at '目标地址' from origin '网页所在地址' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
意为: 从"网页所在地址"向"目标地址"发送的ajax请求,被浏览器的CORS策略阻止。因为来源数据没有符合条件的响应头。
原理: 所有浏览器都有一个"同源策略":CORS
同源策略: Cross Origin Resources Shareing
跨(不同)源头 资源 共享 策略
一个网站的网页中发送的ajax请求,只能访问自己网站提供的数据。不能访问别的网站提供的数据。
现象:
1. 允许发送请求
2. 也能成功收到请求
3. 但浏览器会检查响应头(快递单)中的来源地址:
如果响应头中的来源地址和当前网页所在网站的地址不同,则禁止网页使用响应回来的数据!报错!
只有响应头中的来源地址和当前网页所在网站的地址相同,才允许网页使用响应回来的数据。
跨域请求包括:
1. 域名不同: http://www.a.com下的网站
ajax 请求 -> http://www.b.com
2. 子级域名不同: http://oa.tedu.cn 下的网页
ajax 请求 -> http://hr.tedu.cn
3. 端口不同: http://localhost:5500 下的网页
ajax 请求 -> http://localhost:3000
4. 协议不同: http://12306.cn:80 下的网页
ajax 请求 -> https://12306.cn:443
因为协议不同,其实就是端口号不同
http协议默认是80端口
https 协议默认是443端口
5. 即使同一台及其,相同端口和协议,IP和域名之间互访,也算跨域:
http://localhost:3000下的网页
ajax请求 -> http://127.0.0.1:3000
以上五种情况,都不允许ajax请求数据。
解决ajax跨域问题: 2种方法:
1. CORS: 只需要服务端在返回数据时,在响应头中伪造来源地址,和客户端浏览器地址栏中的地址保持一致。
比如: 127.0.0.1:5500/15_CORS.html中
ajax请求->http://localhost:3000
伪装响应头:{
'Access-Control-Allow-Origin': "127.0.0.1:5500"
我来自
}
可以使用 <- 数据
问题: 伪装响应头时,只写死一个客户端地址,如果将来多个客户端都需要访问这个服务端的数据,就不可兼得。
解决: 'Access-Control-Allow-Origin':"*"
可伪装成任意客户端地址,允许任意客户端都能访问这个服务端的数据。
问题: 每个接口,都需要伪装响应头,每个接口在返回数据前,都要写res.writeHead(200,{ ... })
解决: nodejs中:
安装cors模块: npm i -save cors
并为app.js配置cors中间件
凡是进入app.js的请求,统一,先伪装响应头,再进入路由处理数据
2. JSONP:
前后端分离:
将来的项目都是前端和后端两个项目独立开发
两个项目之间的联系,仅仅靠接口地址
后端: 只负责编写服务端程序,并抛出接口地址,只需要保证接口地址接收参数和返回数据正确即可!
前端: 通过ajax请求,请求服务端接口地址,提交/获取服务端数据,实现页面交互功能。
前后端分离的核心技术: ajax和跨域请求