jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。Jquery提供的诸多API详见 jQuery API 中文文档。
这篇博客参考:jQuery设计思想 - 阮一峰的网络日志 ,记录下 jquery 如何获取/创建/移动元素、如何修改元素属性,以及jQuery 链式操作的实现原理。尤其是链式操作,是jQuery最令人称道、最方便的特点。
1、jQuery 如何获取元素
jQuery的基本设计思想和主要用法,就是"选择某个网页元素,然后对其进行某种操作"。这是它区别于其他Javascript库的根本特点。
-
获取网页元素:
jquery(selector) 或者 $(selector) 。 jquery可以简写为 $ ,selector为选择表达式
选择表达式可以是CSS选择器:
$('div.myClass') // 选择class为myClass的div元素
$('input[name=first]') // 选择name属性等于first的input元素
也可以是jQuery特有的表达式:
$('a:first') //选择网页中第一个a元素
$('tr:odd') //选择表格的奇数行
$('#myForm :input') // 选择表单中的input元素
$('div:visible') //选择可见的div元素
$('div:gt(2)') // 选择所有的div元素,除了前三个
$('div:animated') // 选择当前处于动画状态的div元素
$(':header') // 获取所有的标题元素:<h1> ~ <h6>
$(':animated') // 获取所有的动画元素
$('p:contains(Hello)') // 获取所有包含文本为Hello的<p>元素,中间的文本区分大小写
$(':hidden') // 获取所有的隐藏元素:width和height为0、display:none、type=hidden、
$('[href]') // 属性选择器:获取所有含有属性为href的元素
$('div + p') // 每个div相邻的下一个<p>元素
$('div ~ p') // 获取跟div同级的所有的<p>元素
!!!但要注意:jquery(selector) 操作返回的是一个 API ,包含处理 selector 的方法,这是为了方便进行链式操作。
-
对获取的网页元素进一步筛选:
$('div').has('p'); // 选择包含p元素的div元素
$('div').not('.myClass'); //选择class不等于myClass的div元素
$('div').filter('.myClass'); //选择class等于myClass的div元素
$('div').eq(0); //选择第1个div元素
2、链式操作
这是jQuery最令人称道、最方便的特点。它的原理在于每一步的jQuery操作,返回的都是一个jQuery对象,所以不同操作可以连在一起。
(1)链式调用的代码
$('div').find('h3').eq(2).html('Hello');
分解开来,就是下面这样:
$('div') //找到div元素
.find('h3') //选择其中的h3元素
.eq(2) //选择第3个h3元素
.html('Hello'); //将它的内容改为Hello
jQuery还提供了.end()方法,使得结果集可以后退一步(相对于find() ):
$('div')
.find('h3').eq(2).html('Hello')
.end() //退回到选中所有的h3元素的那一步
(2)原理
链式调用的实现离不开this和elements这两个外部变量,也就是说利用了js闭包。
-
链式调用addClass()是通过return this的形式来实现的
通过对象上的方法最后加上return this,把对象再返回回来,对象就可以继续调用方法,实现链式操作。
const 对象 = {
方法名:function(){
// …
return this; // 实现链式编程的核心this,this的关键字,表示当前对象。
}
}
比如为了能链式调用 addClass() 方法:
window.jQuery=function(selector){
const elements=documents.querySelectorAll(selector)
const api={
addClass(className){
for(let i=0;i<elements.length;i++){
elements[i].classList.add(className)
}
return api // addClass()里返回一个可以调用addClass()的api,这样就能链式调用 addClass()
}//addclass
}//api
return api // 也返回api,但只是为了jQuery()返回值能调用addClass()
}//window.jQuery
addClass()里返回一个可以调用addClass()的api,这样就能链式调用 addClass()
上述代码等同于:
window.jQuery=function(selector){
const elements=documents.querySelectorAll(selector)
return{
addClass(className){
for(let i=0;i<elements.length;i++){
elements[i].classList.add(className)
}
return this
}//addclass
}//return
}//window.jQuery
调用方式:
jQuery('.test').addClass('red').addClass('blue')
可以看出,对于jQuery函数return的结果来说,this和elements都是外部变量。
其中,谁调用addClass(),this就指向谁。
现在结合this再分析下上面调用方式里2次链式调用的具体过程
1、jQuery('.test')返回的对象可以调用addClass('red')
2、因为谁调用addClass(),this就指向谁,故:jQuery('.test').addClass('red') 返回的this指向jQuery('.test')。而结合第1条,jQuery('.test')返回的对象可以调用addClass('red')。因此可以继续调用addClass('blue'),依次类推,链式调用。
最终在 .test 标签上添加 red 类和 blue 类。
-
find()链式调用addClass(),除了借助this以外,还用到了elements这个外部变量
window.jQuery = function(selector) {
const elements=documents.querySelectorAll(selector)
return{
addClass(){},
find(selector) {
let array = [];
for (let i = 0; i < this.elements.length; i++) {
const elements2 = Array.from(this.elements[i].querySelectorAll(selector));
array = array.concat(elements2);
}
array.oldApi = this; // this 就是 旧 api,目的是为了给end()使用
return jQuery(array);
}//find
}//return
}//window.jQuery
调用方式:
jQuery('.test').find('.child').addClass('red').addClass('blue')
1、jQuery('.test')返回的是一个可以处理elements的 api 对象(elements是一个伪数组,装满了被 .test 选中的div)。
2、find('.child') 返回的是一个能在后面链式调用addClass()的api
3、array作参数是为了改变elements,而find()、addClass()均瑶访问elements(闭包:访问函数外部变量),且elements变成了array(array里装的又是.child选中的所有div),故addClass里面的elements也变成了array
因此,可以在每一个.child标签里添加'red'类和'blue'类
parent()也是类似原理。
-
end()需要用到find(){}设置的oldApi
比较复杂,可以看方方老师的代码:dom-2-prototype/jquery.js
3、创建元素
createElement() 是来自核心 JavaScript 的用于创建 HTML 元素的方法。在 jQuery 中,有一些方法可以执行类似的操作:
- append():所选元素的末尾添加元素。
- prepend() :在所选元素的开头添加元素。
- after() :在选定元素之后添加元素。
- before() :在所选元素之前添加元素。
4、插入元素
假定我们选中了一个div元素,需要把它移动到p元素后面。
第一种方法是使用.insertAfter(),把div元素移动p元素后面:
$('div').insertAfter($('p'));
第二种方法是使用.after(),把p元素加到div元素前面:
$('p').after($('div'));
但是两种方法返回的API显然不一样,前者返回处理p标签的API,后者返回处理div标签的API。
插入操作有以下四对:
.insertAfter()和.after():在现存元素的外部,从后面插入元素
.insertBefore()和.before():在现存元素的外部,从前面插入元素
.appendTo()和.append():在现存元素的内部,从后面插入元素
.prependTo()和.prepend():在现存元素的内部,从前面插入元素
5、复制/删除操作
复制元素使用.clone()。
删除元素使用.remove()和.detach()。前者不保留被删除元素的事件,后者保留,有利于重新插入文档时使用。
清空元素内容(但是不删除该元素)使用.empty()。
6、修改元素属性
jQuery使用同一个函数,来完成取值和赋值,具体取值还是赋值,取决于函数的参数,比如
$("div").text() //表示获取div的内容
$("div").text('新的内容') //设置div的内容
需要注意的是,如果结果集包含多个元素,那么赋值的时候,将对其中所有的元素赋值;取值的时候,则是只取出第一个元素的值(.text()例外,它取出所有元素的text内容)。
使用attr()可以修改元素属性:
$('div').attr('class','red') // 有则改 无则加 class为red
removeAttr()可以删除属性
$('div').removeAttr('name') // 删除name属性