JS基础 - JQuery

代码:
https://github.com/Mcguffen/jQuery-demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<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>
</body>
</html>

封装函数

1
function getSiblings(node){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getSiblings(node){
// 将目标节点item3的兄弟节点放入一个数组
var allChildren = node.parentNode.children
// 伪数组 一个哈希 没有push()方法
// 理解array[array.length]
var array = { length: 0}
for(let i = 0 ; i < allChildren.length; i++){
if(allChildren[i] !== item3){
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
}


console.log(getSiblings(item3));

再封装一个

1
function addClass(node, classes){}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function addClasses(node,classes){
// 为了又可以添加class又能remove class true为添加 false为不添加
var classes = {
'a': true,
'b':false,
'c':true
}
for(let key in classes){
var value = classes[key]
if(value){
node.classList.add(key);
}else{
node.classList.remove(key)
}
}


}

function addClasses(node, classes)代码优化

1
2
3
4
5
6
7
8
9
10
11
12
function addClasses(node, classes) {
// 为了又可以添加class又能remove class true为添加 false为不添加

for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
node.clasList[methodName](key)
}


}
addClasses(item1, { 'a': true,'b': true, 'c': true }

命名空间

为了让上面两个方法有关联性 因为这两个方法都是操作node
这种我认为好用的套路 也想让别人用 就用命名空间 命名后给别人用
我们就命名为ydom

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
window.ydom = {}
ydom.getSiblings= function(node) {
// 将目标节点的兄弟节点放入一个数组
var allChildren = node.parentNode.children
// 伪数组 一个哈希 没有push()方法
// 理解array[array.length]
var array = { length: 0 }
for (let i = 0; i < allChildren.length; i++) {
if (allChildren[i] !== node) {
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
}
ydom.addClasses =function addClasses(node, classes) {
// 为了又可以添加class又能remove class true为添加 false为不添加

for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
node.classList[methodName](key)

// obj.x()
// obj['x']() 等价
}


}

ydom.getSiblings(item2)
console.log(ydom.getSiblings(item2));
ydom.addClasses(item2,{'a':false,'b':true,'c':true})
console.log(item2);

能不能把 node 放前面

1
2
3
node.getSiblings()

node.addClass()
  1. 方法一:扩展 Node 接口
    直接在 Node.prototype 上加函数

    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

    // 上面的问题是不好用 每次还要ydom.xx()每次都要ydom
    // ydom.getSiblings(item2)
    // 我想 item2.getSiblings()

    // 原型链
    // 给Node的共有属性添加一个getSibs()的方法 那么作为节点item2肯定有这个节点。
    Node.prototype.getSiblings = function(){
    // 将目标节点的兄弟节点放入一个数组
    // node 就是this this就是谁调用就是谁
    var allChildren = this.parentNode.children
    // 伪数组 一个哈希 没有push()方法
    // 理解array[array.length]
    var array = { length: 0 }
    for (let i = 0; i < allChildren.length; i++) {
    if (allChildren[i] !== this) {
    array[array.length] = allChildren[i]
    array.length += 1
    }
    }
    return array
    }
    item2.getSiblings()
    console.log(item2.getSiblings());
    console.dir(item2);
    // 原型链
    // 给Node的共有属性添加一个getSibs()的方法 那么作为节点item2肯定有这个节点。
    Node.prototype.addClasses = function(classes){
    // 为了又可以添加class又能remove class true为添加 false为不添加

    for (let key in classes) {
    var value = classes[key]
    var methodName = value ? 'add' : 'remove'
    this.classList[methodName](key)
    }
    }
    item2.addClasses({'a':true,"b":false,"c":false})
    console.log(item2);
  2. 方法二:新的接口 BetterNode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Node2(node){
return {
element: node,
getSiblings: function(){
},
addClass: function(){
}
}
}

let node =document.getElementById('x')
let node2 = Node2(node)
node2.getSiblings()
node2.addClass()

上面修改原型链的方法也不妥 现在问题是 别人也可以重修这个共有属性 大家相互覆盖
那怎么办呢? 上面大家都修改同一个Node的原型链 那么 我们新创建一个名叫别的Node不就行了?
至于叫什么 叫啥都行 就反正不冲突就行 你叫jQuery都行。。。
比如叫Yode行不行 我们自己弄一个构造函数 你给我一个节点我返回给你一个新的节点,新节点带有2个函数

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

window.Yode = function(node){
return {
getSiblings: function(){
// 这个时候不能用this了 this是yode1 我们操作的是node
var allChildren = node.parentNode.children
var array = { length: 0 }
for (let i = 0; i < allChildren.length; i++) {
if (allChildren[i] !== node) {
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
},
addClasses: function(classes){
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
// 这里的this也改成node 因为this是yode1 而我们操作的是node
node.classList[methodName](key)
}
}
}
}
var yode1 = Yode(item1)
yode1.getSiblings()
console.log(yode1);
console.log(yode1.getSiblings()
);
yode1.addClasses({'a':true,'b':false,'c':false})
// 之前我们是
// ydom.getSiblings(item1)
// ydom.addClasses(item1)
// 你说区别在哪里 不就是多了个构造函数

第二种叫做「无侵入」。

把 Node2 改个名字吧 大胆点就叫jQuery

名字不重要 上面我们都叫Yode了 你也可以叫jQuery 比如下面这样
反正核心是 通过Yode这个构造函数得到一个新的节点 这个节点带有构造函数的两个方法给你用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function jQuery(node){
return {
element: node,
getSiblings: function(){

},
addClass: function(){
}
}
}
let node =document.getElementById('x')
let node2 =jQuery(node)
node2.getSiblings()
node2.addClass()

所以叫啥都行

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
        window.jQuery = function(node){
return {
getSiblings: function(){
// 这个时候不能用this了 this是yode1 我们操作的是node
var allChildren = node.parentNode.children
var array = { length: 0 }
for (let i = 0; i < allChildren.length; i++) {
if (allChildren[i] !== node) {
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
},
addClasses: function(classes){
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
// 这里的this也改成node 因为this是yode1 而我们操作的是node
node.classList[methodName](key)
}
}
}
}
var yode1 = jQuery(item1)
yode1.getSiblings()
console.log(yode1);
console.log(yode1.getSiblings()
);
yode1.addClasses({'a':true,'b':false,'c':false})
// 之前我们是
// ydom.getSiblings(item1)
// ydom.addClasses(item1)
// 你说区别在哪里 不就是多了个构造函数

再给个缩写吧 alias 比如window.$ = jQuery

1
window.$ = jQuery

**改进零:接受字符串是选择器

那么jQuery更厉害一点 参数不局限于节点也可以接受一个字符串,这个字符串可以是一个选择器
我们改进一下参数接受节点或者选择器

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
window.jQuery = function(nodeOrSelector){
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector)
console.log(node);
}else{
node = nodeOrSelector
}
return {
getSiblings: function(){
// 这个时候不能用this了 this是yode1 我们操作的是node
var allChildren = node.parentNode.children
var array = { length: 0 }
for (let i = 0; i < allChildren.length; i++) {
if (allChildren[i] !== node) {
array[array.length] = allChildren[i]
array.length += 1
}
}
return array
},
addClasses: function(classes){
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
// 这里的this也改成node 因为this是yode1 而我们操作的是node
node.classList[methodName](key)
}
}
}
}
// 再来个缩写 alias
window.$ = jQuery
var yode2 = $('#item2')
yode2.getSiblings()
console.log(yode2);

console.log(yode2.getSiblings());

yode2.addClasses({'a':true,'b':false,'c':false})

核心就是这个 判断一下传的是字符串选择器还是节点

1
2
3
4
5
6
if(typeof nodeOrSelector === 'string'){
node = document.querySelector(nodeOrSelector)
console.log(node);
}else{
node = nodeOrSelector
}

改进一:改掉 document.getElementById

改进二:接受多个 node

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
// 那么怎么操作多个节点呢?
window.jQuery = function(nodeOrSelector){
let nodes = {}
if(typeof nodeOrSelector === 'string'){
// 多个节点
let temp = document.querySelectorAll(nodeOrSelector)// 多个节点 伪数组
for(let i=0;i<temp.length;i++){
nodes[i] = temp[i]
}
nodes.length = temp.length
}else if(nodeOrSelector instanceof Node){
// 只传1个节点 我们保证返回结果一致 也用伪数组
nodes = {
0: nodeOrSelector,
length: 1
}
}
//
nodes.getSiblings = function(){

}
nodes.addClasses = function(classes){
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
for(let i = 0; i<nodes.length; i++){
nodes[i].classList[methodName](key)

}

}

}
return nodes
}
// 再来个缩写 alias
window.$ = jQuery
var yodeAll = $('ul>li')
yodeAll.addClasses({'a':true,'b':false,'c':false})
console.log(yodeAll);

改进三:添加 .html() .text() .css() 等

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
48
49
50
51
52
53
54
55
56
57
58
59
window.jQuery = function(nodeOrSelector){
let nodes = {}
if(typeof nodeOrSelector === 'string'){
// 多个节点
let temp = document.querySelectorAll(nodeOrSelector)// 多个节点 伪数组
for(let i=0;i<temp.length;i++){
nodes[i] = temp[i]
}
nodes.length = temp.length
}else if(nodeOrSelector instanceof Node){
// 只传1个节点 我们保证返回结果一致 也用伪数组
nodes = {
0: nodeOrSelector,
length: 1
}
}
// 特殊的 单独一个节点
nodes.getSiblings = function(){

}
nodes.addClasses = function(classes){
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
for(let i = 0; i<nodes.length; i++){
nodes[i].classList[methodName](key)

}

}

}
// 批量获取节点们的文本
nodes.getAllTexts = function(){
let texts = []
for(let i = 0; i<nodes.length; i++){
texts.push(nodes[i].textContent)
}
return texts
}
// 批量修改textss
nodes.setAllTexts = function(text){
let texts = []
for(let i = 0; i<nodes.length; i++){
nodes[i].textContent = text
}
return texts
}

return nodes
}

// 再来个缩写 alias
window.$ = jQuery
var yodeAll = $('ul>li')
yodeAll.addClasses({'a':true,'b':false,'c':false})
console.log(yodeAll);
console.log(yodeAll.getAllTexts())
yodeAll.setAllTexts('hello')

优化
合并上面获取和修改全部节点文本内容的方法 不给参数就是获取全部文本内容 给参数就是修改全部文本内容
jQuery有大量api就是这么设计的,给参数就是修改,不给就是获取。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
window.jQuery = function(nodeOrSelector){
let nodes = {}
if(typeof nodeOrSelector === 'string'){
// 多个节点
let temp = document.querySelectorAll(nodeOrSelector)// 多个节点 伪数组
for(let i=0;i<temp.length;i++){
nodes[i] = temp[i]
}
nodes.length = temp.length
}else if(nodeOrSelector instanceof Node){
// 只传1个节点 我们保证返回结果一致 也用伪数组
nodes = {
0: nodeOrSelector,
length: 1
}
}
// 特殊的 单独一个节点
nodes.getSiblings = function(){

}
nodes.addClasses = function(classes){
for (let key in classes) {
var value = classes[key]
var methodName = value ? 'add' : 'remove'
for(let i = 0; i<nodes.length; i++){
nodes[i].classList[methodName](key)

}

}

}

// 合并上面获取和修改全部节点文本内容的方法 不给参数就是获取全部文本内容 给参数就是修改全部文本内容
nodes.text = function(text){
// 没给值 就是获取
if(text === undefined){
let texts = []
for(let i = 0; i<nodes.length; i++){
texts.push(nodes[i].textContent)
}
return texts
}else{
// 给值就是修改
let texts = []
for(let i = 0; i<nodes.length; i++){
nodes[i].textContent = text
}
return texts
}
}
return nodes
}

// 再来个缩写 alias
window.$ = jQuery
var yodeAll = $('ul>li')
yodeAll.addClasses({'a':true,'b':false,'c':false})
console.log(yodeAll);
// 获取全部文本内容
yodeAll.text()
console.log(yodeAll.text());
// 修改全部文本内容 是不是比刚才方便
yodeAll.text('hi')

JQuery 优点

  1. jQuery 在兼容性方面做得很好,1.7 版本兼容到 IE 6

  2. jQuery 还有动画、AJAX 等模块,不止 DOM 操作

  3. jQuery 的功能更丰富

  4. jQuery 使用了 prototype,我们没有使用,等讲了 new 之后再用

JQuery 实现一个轮播

  1. HTML、CSS、JS 内容、样式与行为分离

  2. 轮播的思路

    i. 思路1:滚来滚去

    ii. 思路2:用局部画面骗用户

  3. 封装思路

    i. 从 API 开始思考

    ii. 尽量能让使用者猜到

代码:

重构前:

html

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
<!DOCTYPE html>
<html>

<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
.images{
display: flex;
align-items: flex-start;
transition: all 0.5s;
}
.images > img{
vertical-align: top;
}
.window{
width: 300px;
overflow: hidden;
}
</style>
</head>

<body>
<div class="window">
<div class="images" id=images>
<img src="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width=300 alt="图片1" height=200>
<img src="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width=300 alt="" height=200>
<img src="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width=300 alt="" height=200>
<img src="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width=300 alt="" height=200>
</div>
</div>
<span id=buttons>
<span>第1张</span>
<span>第2张</span>
<span>第3张</span>
<span>第4张</span>
</span>
</body>

</html>

js

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
var allButtons = $('#buttons > span')

for (let i = 0; i < allButtons.length; i++) {
$(allButtons[i]).on('click', function(x) {
console.log('hi')
var index = $(x.currentTarget).index()
var p = index * -300
$('#images').css({
transform: 'translate(' + p + 'px)'
})
n = index
allButtons.eq(n)
.addClass('red')
.siblings('.red').removeClass('red')
})
}



var n = 0;
var size = allButtons.length
allButtons.eq(n % size).trigger('click')
.addClass('red')
.siblings('.red').removeClass('red')

var timerId = setInterval(() => {
n += 1
allButtons.eq(n % size).trigger('click')
.addClass('red')
.siblings('.red').removeClass('red')
}, 1000)

$('.window').on('mouseenter', function() {
window.clearInterval(timerId)
})

$('.window').on('mouseleave', function() {
timerId = setInterval(() => {
n += 1
allButtons.eq(n % size).trigger('click')
.addClass('red')
.siblings('.red').removeClass('red')
}, 3000)
})

重构后:

html

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
<!DOCTYPE html>
<html>

<head>
<script src="//code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
<style>
.images{
display: flex;
align-items: flex-start;
transition: all 0.5s;
}
.images > img{
vertical-align: top;
}
.window{
width: 300px;
overflow: hidden;
}
</style>
</head>

<body>
<div class="window">
<div class="images" id=images>
<img src="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width=300 alt="图片1" height=200>
<img src="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width=300 alt="" height=200>
<img src="https://fthmb.tqn.com/0ui_Zw01Ht9NHJkSBlqOIC1IH44=/960x0/filters:no_upscale()/yorkshire-terrier-583788122-581630e85f9b581c0b018a00.jpg" width=300 alt="" height=200>
<img src="https://canna-pet.com/wp-content/uploads/2017/06/20-longest-living-dog-breeds_canna-pet-e1498599846169.jpg" width=300 alt="" height=200>
</div>
</div>
<span id=buttons>
<span>第1张</span>
<span>第2张</span>
<span>第3张</span>
<span>第4张</span>
</span>
</body>

</html>

js

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
var allButtons = $('#buttons > span')

for (let i = 0; i < allButtons.length; i++) {
$(allButtons[i]).on('click', function(x) {
var index = $(x.currentTarget).index()
var p = index * -300
$('#images').css({
transform: 'translate(' + p + 'px)'
})
n = index
activeButton(allButtons.eq(n))
})
}



var n = 0;
var size = allButtons.length
allButtons.eq(n % size).trigger('click')


var timerId = setTimer()

function setTimer() {
return setInterval(() => {
n += 1
allButtons.eq(n % size).trigger('click')
}, 3000)
}

function activeButton($button) {
$button
.addClass('red')
.siblings('.red').removeClass('red')
}

$('.window').on('mouseenter', function() {
window.clearInterval(timerId)
})

$('.window').on('mouseleave', function() {
timerId = setTimer()
})
编辑