jQuery入门

介绍一下jQuery常用方法,以及封装几个方法。

一、封装函数

1)function getSublings(node)

获取一个节点的所有兄弟元素,除了自己。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
<ul>
<li id="item1">标题1</li>
<li id="item2">标题2</li>
<li id="item3">标题3</li>
<li id="item4">标题4</li>
<li id="item5">标题5</li>
</ul>
...
let allChilds = item3.parentNode.children;
let siblingArr = {length : 0}; // 此变量为伪数组,无push方法
for(let i = 0;i<allChilds.length;i++){
if(allChilds[i] != item3){
siblingArr[siblingArr.length] = allChilds[i];
siblingArr.length++;
}
}
console.log(siblingArr); //输出{0: li#item1, 1: li#item2, 2: li#item4, 3: li#item5, length: 4}
...

以上是原始代码,接下来对此代码进行初步的封装:

1
2
3
4
5
6
7
8
9
10
11
12
function getSiblings(node){           //方法定义
let allChilds = node.parentNode.children;
let siblingArr = {length : 0};
for(let i = 0;i<allChilds.length;i++){
if(allChilds[i] !== node){
siblingArr[siblingArr.length] = allChilds[i];
siblingArr.length++;
}
}
return siblingArr;
}
console.log(getSiblings(item3)); //方法调用,输出{0: li#item1, 1: li#item2, 2: li#item4, 3: li#item5, length: 4}

以上OK。

2)function addClass(node,classed)

DOM中addClass每次只能添加一个,这个方法实现能够一次添加多个。
根据以上代码继续写:

1
2
3
4
5
6
7
8
let classes = {'a':true,'b':false,'c':true};
for(var key in classes){
if(classes[key]){
item3.classList.add(key);
}else{
item3.classList.remove(key);
}
}

此时item3的class=’a c’;
对上述添加类名的代码进行封装:

1
2
3
4
5
6
7
8
9
10
function addClass(node,classes){
for(var key in classes){
if(classes[key]){
node.classList.add(key);
}else{
node.classList.remove(key);
}
}
}
addClass(item3,{'a':false,'b':true}); //此时item3的class='b'

3)代码优化

代码优化原则
如果出现类似代码,那么就存在优化的可能。
所以,对以上添加类名的代码可进行优化:

1
2
3
4
5
6
7
...
for(var key in classes){
let val = classes[key];
let methodName = val ? 'add' : 'remove';
node.classList[methodName](val);
}
...

4)命名空间

将封装的两个函数放到同一个命名空间下,每次通过此命名空间调用方法。
比如Document和Element都会有各自的属性和方法,如果二者不存在继承关系,那么Element不能使用Document中的方法/属性。命名空间同理,我们把封装的函数放在一个对象中,通过此对象调用方法,如:

1
2
3
4
5
6
window.sDom = {};
sDom.getSiblings = getSiblings;
sDom.addClass = addClass;

console.log(sDom.getSiblings(item2));
sDom.addClass(item2,{'summer':true,'winter':false})

以上sDom就是命名空间。

5)代码优化

通常,我们对Element的属性获取/方法调用都是以Element.parentNode、Element.querySelector()等等,而以上我们通过命名空间封装的方法需要使用sDom.getSiblings(node,{…class…})来操作,为了统一使用方法,我们将封装的方法直接添加的Element的原型中,那么所有的Element就都会继承此方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Node.prototype.getSiblings = function(){
let allChilds = this.parentNode.children;
let siblingArr = {length : 0};
for(let i = 0;i<allChilds.length;i++){
if(allChilds[i] != this){
siblingArr[siblingArr.length] = allChilds[i];
siblingArr.length++;
}
}
return siblingArr;
}
Node.prototype.addClass = function(classes){
for(var key in classes){
let val = classes[key];
let methodName = val ? 'add' : 'remove';
this.classList[methodName](key);
}
}
console.log(item4.getSiblings());
item3.addClass({'summer':true,'windy':true})
//以上代码如果使用call()调用的话,方法为:
item4.getSiblings.call(item4);
item3.addClass(item3,{......})
//以上为显示调用this

6)代码优化

上述代码功能已经完善,但是存在一个问题,如果当前项目为多人开发,A在Node原型上定义了getSiblings方法,此时如果B在原型上定义了想相同名字不同功能的方法,那么A的方法将被覆盖。为避免此问题,提供一下解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
window.Node2 = function(node){
return {
getSiblings : function(){
let allChilds = node.parentNode.children;
let siblingArr = {length : 0};
for(let i = 0;i<allChilds.length;i++){
if(allChilds[i] != node){
siblingArr[siblingArr.length] = allChilds[i];
siblingArr.length++;
}
}
return siblingArr;
},
addClass : function(classes){
for(var key in classes){
let val = classes[key];
let methodName = val ? 'add' : 'remove';
node.classList[methodName](key);
}
}
}
}
let node2 = Node2(item2);
console.log(node2.getSiblings());
node2.addClass({'summer':true,'windy':true})

以上,如果将Node2换成jQuery就可以看成是jQuery源码,

7)代码优化

在jQuery中,选择其可以直接传node还可以传其他的,具体操作如下,根据上述代码修改部分:

1
2
3
4
5
6
7
8
9
10
11
12
window.jQuery = function(nodeorSelector){
let node;
if(typeof nodeorSelector == "string"){
node = document.querySelector(nodeorSelector);
}else{
node = nodeorSelector;
}
.......相同的地方就不再重复.......
}
let node2 = jQuery('#item2'); // 此处可以直接传递id选择器,或者输入Node2('ul >li:nth-child(2)')
console.log(node2.getSiblings());
node2.addClass({'red':true,'windy':true})

8)继续优化。。。

7)中的代码都是建立在传递一个节点的基础上的,然而大多数情况下,传递的节点数为一个及其以上,直接上代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
window.jQuery = function(nodeorSelector){
let nodes = {}; // 伪数组
if(typeof nodeorSelector == "string"){
let temps = document.querySelectorAll(nodeorSelector);
for(let i = 0;i<temps.length;i++){
nodes[i] = temps[i];
}
nodes.length = temps.length;
}else{
nodes = {
0 : nodeorSelector,
length:1
};
}
nodes.getSiblings = function(){
//这个要求比较多,暂时不进行过多的介绍
}
nodes.addClass = function(classes){
for(let i = 0;i<nodes.length;i++){
for(var key in classes){
let val = classes[key];
let methodName = val ? 'add' : 'remove';
nodes[i].classList[methodName](key);
}
}

}
//获取文本
nodes.getText = function(){
let texts = [];
for(let i = 0;i<nodes.length;i++){
texts.push(nodes[i].textContent);
}
return texts;
}
//设置文本
nodes.setText = function(txt){
for(let i = 0;i<nodes.length;i++){
nodes[i].textContent = txt;
}
return nodes;
}
let node2 = jQuery('ul>li');
//console.log(node2.getSiblings());
node2.addClass({'blue':true,'windy':true})
console.log(node2.getText()); //输出:(5) ["标题1", "标题2", "标题3", "标题4", "标题5"]
node2.setText('hello'); // li的内容全部变为hello

9)最后一次优化了

这次优化是优化关于getText()和setText()方法的。在jQury中,关于set()/get()方法是合并到一起的,如jQuery中的html()方法,当传参的时候表示赋值,未传参的时候表示取值。所以将以上代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
......
//只修改set/get部分
nodes.text = function(val){
if(val === undefined){
let 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 = val;
}
}
}
......
let node2 = jQuery('ul>li');
console.log(node2.text())
node2.text('1111');

二、Tips

1)DOM和jQuery对象关系和转换

1
2
3
4
5
...
<div id=x></div>
...
var div = document.getElementById('x')
var $div = $('#x')

iv 和 $div 的联系是:

  • $(div) 可以将 div 封装成一个 jQuery 对象,就跟 $div 一样
  • $div[0] === div ,$div 的第一项就是 div

div 和 $div 的区别是:

  • div 的属性和方法有 childNodes firstChid nodeType 等
  • $div 的 属性和方法有 addClass removeClass toggleClass 等