jQuery入门
什么是jQuery
首先,什么是jQuery?jQuery中文文档的定义是jQuery是一个高效、精简且功能丰富的JavaScript工具库
,除去一些修饰语,那它就是一个工具库。库是什么?库是特定种类的API。我们知道函数的封装是为了消除冗余代码,如果有一堆函数实现的功能有某种内在的联系,比如说都是对元素的操作,那我们是不是可以把它们放到同一个地方,与其他函数区分开来?这就形成了一个库。
让我们先抛开jQuery的定义,来写一个工具库。
DOM并没有提供获取所有兄弟姐妹元素的API,所以我们来写一个API,让它能获取到某个元素的兄弟姐妹并返回一个伪数组。
function getSiblings(node){ //传入一个节点,获取这个节点的兄弟姐妹
var temp = node.parentElement.children
var array = {length: 0}
for(let i = 0; i < temp.length; i++){
if(temp[i] !== node){
array[array.length] = temp[i] //由于伪数组没有push方法,所以我们直接用对象属性赋值来实现
array.length++
}
}
return array //返回这个存了兄弟姐妹的伪数组
}
DOM只提供了classList.add和classList.remove进行类的添加和删除,但如果我们要同时添加多个类的话这个方法就显得很笨重了,所以我们再写一个关于元素类的API,方便我们对其进行批量添加和删除。
function addClass(node, classes) { //classes传入的是一个对象,key为要添加/删除的类名,value为true/false。
for (var key in classes) {
classes[key]? node.classList.add(key): node.classList.remove(key)
/*
* 当然那一句有点长如果你看着不舒服可以换一种写法:
var methodName = classes[key]?'add':'remove'
node.classList[methodName](key)
*
*/
}
}
喏,现在我们有了两个函数,这两个函数都是对节点的操作,但是它们现在看上去毫无关系,所以怎么样可以让别人一眼看出来这两个是同一类型的东西呢?放到同一个命名空间下。
window.cydom = {}
cydom.getSiblings = getSiblings
cydom.addClass = addClass
这样把它们都放到cydom下,看上去就比较好辨认了。并且我们可以通过cydom.getSiblings来调用getSiblings。
我们调用它用的是cydom.getSiblings(node)
和cydom.addClass(node,{'red': true})
,如你所见,所有的方法都必定要传一个参数node。所以可不可以变成node.getSiblings()
这种形式呢?不知道你们记不记得(1).toString()
?是的,把这个方法放到它的原型链上。我们要操作的是节点,所以应该把这两个方法放在Node.prototype
里边。也就是如下形式。函数内部使用node参数的时候用this代替。
Node.prototype.getSiblings = function(){...}
Node.prototype.addClass = function(){...}
但是这样不够安全。怎么说不够安全呢?如果另外有人写了一个函数和这个功能不同,但是取了相同的名字,那么你的这个函数就会被覆盖,或者你可能覆盖别人的函数。所以呢?更好的方法当然是封装了。把这些利用DOM实现的但是比DOM好用的方法用一个构造函数(Node2)封装起来,这样我们通过这个构造函数构造出来的对象就可以直接调用这些方法了。
window.Node2 = function(node) { //接收一个元素,返回一个新的对象,这个对象有着新的API
return { //把这两个方法放到一个hash里边返回
getSiblings: function() {
var temp = node.parentElement.children
var array = {
length: 0
}
for (let i = 0; i < temp.length; i++) {
if (temp[i] !== this) {
array[array.length] = temp[i]
array.length++
}
}
return array
},
addClass: function(classes) {
for (var key in classes) {
var methodName = classes[key] ? 'add' : 'remove'
node.classList[methodName](key)
}
}
}
}
//调用
var newnode = Node2(oldnode)
newnode.getSiblings()
当然,jQuery做到的不只是传入一个元素,还可以传入一个选择器。在前边做个小判断就好了。
window.jQuery = function(nodeOrSelector){
var node
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector)
}else{
node = nodeOrSelector
}
...
}
那如果我们想同时操作多个节点呢?把node变成伪数组nodes。
window.jQuery = function(nodeOrSelector) {
var nodes = {}
if (typeof nodeOrSelector === 'string') {
nodes = document.querySelectorAll(nodeOrSelector)
} else if (nodeOrSelector instanceof Node) {
nodes = {
0: nodeOrSelector,
length: 1
}
}
return { //或者可以return nodes,把addClass挂到nodes下,即nodes.addClass = function(){}
addClass: function(classes) {
for (var key in classes) {
let methodName = classes[key] ? 'add' : 'remove'
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList[methodName](key)
}
}
}
}
}
jQuery还可以通过判断实现多态,比如我们想要实现一个text函数,当它没有参数的时候获取textContent,有参数的话设置textContent。像这样
nodes.text = function(text) { //设置和获取文字
if (text === undefined) {//不传参数的话默认是undefined
var texts = []
for (let i = 0; i < nodes.length; i++) {
texts.push(nodes[i].textContent)
}
return texts
} else {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
}
}
好了,让我们回到刚开始的问题,什么是jQuery?jQuery实际上就是一个构造函数,它接受一个参数(可以是元素也可以是选择器),然后返回一个对象来操作节点。
jQuery next
jQuery就这么简单?不是的。除了DOM操作外,它还有动画、AJAX等模块,功能更加丰富。它使用了prototype,我们这里没有提到。并且它的兼容性很好。
不过,知道了什么是jQuery之后算是入门了,我们就可以直接看jQuery API了。
以及,我们经常看到的$
就是这里的jQuery
,只需要设置window.$ = jQuery
即可。还有一个习俗:如果一个对象是由jQuery构造出来的,那就在这个对象前边加上$,让我们知道这个对象是由jQuery构造出来的,这样我们可以用jQuery的API而不是DOM的API。