简单实现 jQuery 中的几个 API

背景

对于操作 DOM 来说,jQuery 是非常方便的一个库,虽然如今随着 React, Vue 之类框架的流行,jQuery 用得越来越少了,但是其中很多思想还是非常值得我们学习的,这篇文章将介绍如何实现 jQuery 中的几个 api 。

任务

首先说一下要实现 API :

  1. $():根据选择器构造一个 jQuery 对象
  2. addClass():为每个匹配的元素添加指定的样式类名
  3. text():获取匹配元素集合中每个元素的合并文本,设置匹配元素集合中每个元素的文本内容为指定的文本内容。
  4. css():获取匹配元素集合中的第一个元素的样式属性的值设置每个匹配元素的一个或多个CSS属性。

实现步骤

构造 jQuery 对象

接受一个 node 或选择器,构造并返回一个类数组对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function init(selector){
let nodes = {}

if(typeof selector === 'string'){
// 利用temp去掉多余的原型,直接指向object.prototype
let temp = document.querySelectorAll(selector)
for(let i = 0; i<temp.length; i++){
nodes[i] = temp[i]
}
nodes.length = temp.length
}else if(selector instanceof Node){
nodes = {
0: selector,
length:1
}
}

return nodes
}

addClass

接受一个或多个 class ,遍历每个元素,为元素添加 class 。

1
2
3
4
5
function addClass(className){
for(let i = 0; i<nodes.length; i++){
nodes[i].classList.add(className)
}
}

text

如果有接受参数,则为所有匹配元素设置文本内容;如果没有接受参数,则返回所有匹配元素文本的集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
function text(content){
if(content === 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 = content
}
}
}

css

css() 接受propertynamevalue两个参数,遍历所有匹配元素,设置指定的 css 属性;如果 value为空,并且propertynamestring,则返回第一个元素指定的 css 属性值;如果propertynameobject,如{"propertyname":"value","propertyname":"value",...},则遍历所有匹配元素和propertyname,设置指定的 css 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function css(propertyname,value){
if(value === undefined){
if(typeof propertyname === 'string'){
return window.getComputedStyle(nodes[0],null).getPropertyValue(propertyname)
}else if(typeof propertyname === 'object'){
for(let i = 0; i<nodes.length; i++){
for(let style in propertyname){
nodes[i].setAttribute('style', style+":"+propertyname[style])
}
}
}
}else{
for(let i = 0; i<nodes.length; i++){
nodes[i].setAttribute('style',propertyname+":"+value)
}
}
}

封装

最终代码:

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
window.jQuery = function(selector){
let nodes = {}

if(typeof selector === 'string'){
let temp = document.querySelectorAll(selector)
for(let i = 0; i<temp.length; i++){
nodes[i] = temp[i]
}
nodes.length = temp.length
}else if(selector instanceof Node){
nodes = {
0: selector,
length:1
}
}

nodes.addClass = function(className){
for(let i = 0; i<nodes.length; i++){
nodes[i].classList.add(className)
}
return this
}

nodes.text = function(content){
if(content === 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 = content
}
}
return this
}

nodes.css = function(propertyname,value){
if(value === undefined){
if(typeof propertyname === 'string'){
return window.getComputedStyle(nodes[0],null).getPropertyValue(propertyname)
}else if(typeof propertyname === 'object'){
for(let i = 0; i<nodes.length; i++){
for(let style in propertyname){
nodes[i].setAttribute('style', style+":"+propertyname[style])
}
}
}
}else{
for(let i = 0; i<nodes.length; i++){
nodes[i].setAttribute('style',propertyname+":"+value)
}
}
return this
}

return nodes
}

window.$ = jQuery

效果测试

下面的代码是获取 id 为 wraper下 class 为item的元素,设置文本内容为 hello ,并添加 font-size:20px的 css 样式,最后添加 class red

1
$("#wraper > .item").text("hello").css("font-size","20px").addClass("red")