前言:眾所周知,原生JS的DOM API大多都是很難用的,而在十二年前,已經有了一個很實用的庫,它封裝了很多好用的方法,讓JavaScript更有易用性,如今全世界至少70%的網站都在使用,這個庫就是jQuery。為了更容易理解jQuery的實現過程,這篇文章將用原生JS實現jQuery的元素選擇、添加類、獲取/設置文字這幾個方法,當然jQuery的實際實現比這個要復雜的多,本文不做深入研究。
1、實現元素選擇
- 首先我們需要認識到的是,jQuery實際上就是一個函數,所以最基本的結構應該是這樣的:
windos.jQuery = function(){
}
window.$ = window.jQuery // 實現類jQuery寫法 $()
- 其次我們需要判斷通過該函數傳進來的參數是選擇器還是DOM節點:是選擇器的話,需要使用
document.querySelectorAll()
來獲取元素,然后返回一個對象用來后續的操作;如果是DOM節點,為了保持操作的一致,也需要將其變為一個對象;如果先用一個變量獲取DOM的集合,再將其以參數傳入,也會返回一個對象(var boxes = document.getElementsByTagName('div')
,再將boxes傳入,進入最后一個分支)。具體代碼如下:
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){
nodes={
0:nodeOrSelector,
length:1
}
}
// 傳入參數為DOM節點集合
else{
for(let i = 0;i<nodeOrSelector.length;i++){
nodes[i] = nodeOrSelector[i]
}
nodes['length'] = nodeOrSelector.length
}
return nodes
}
2、實現獲取/設置元素的文本
封裝一個Text()
函數,操作的是轉化過的nodes
對象,如果不傳參,則獲取元素的文本,如果傳參(參數需要是字符串),設置元素的文本為所傳的參數(其中,如果元素是多個節點的集合,那么會為所有的元素都設置文本)。
nodes.Text = function(text){
if(text === undefined){
let texts = []
for(let i = 0;i<nodes.length;i++){
texts[i] = nodes[i].textContent
}
return texts
}else{
for(let i = 0;i<nodes.length;i++){
nodes[i].textContent = text
}
}
}
3、實現為元素添加/刪除類
封裝一個addClass()
函數,操作的轉化過的nodes對象,傳入的參數需要是一個對象,對象的鍵名是需要操作的類,對象的鍵值是操作類型(如{'red':true,'green':false}
表示添加類名為red
的類,同時刪除類名為green
的類)
- 首先使用
for in
循環遍歷傳入的參數,然后使用for
循環遍歷nodes
節點,對每一個元素都要進行操作,判斷鍵值是true
還是false
,前者則添加類,后者則刪除類:
nodes.addClass = function(classes){
for(let key in classes){
for(let i = 0;i<nodes.length;i++){
if(classes[key]){
nodes[i].classList.add(key)
}else{
nodes[i].classList.remove(key)
}
}
}
}
觀察上面的代碼,發現關于if
判斷語句中的兩個分支有相似的地方,而只要有相似的代碼,就有優化的空間,所以進行下面的優化:
nodes.addClass = function(classes){
for(let key in classes){
for(let i = 0;i<nodes.length;i++){
let methodName = classes[key] ? 'add' : 'remove'
nodes[i].classList[methodName](key)
}
}
}
4、完整代碼
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
console.log(1)
} else if (nodeOrSelector instanceof Node) {
nodes = {
0: nodeOrSelector,
length: 1
}
console.log(2)
} else {
for (let i = 0; i < nodeOrSelector.length; i++) {
nodes[i] = nodeOrSelector[i]
}
nodes['length'] = nodeOrSelector.length
console.log(3)
}
nodes.Text = function(text) {
if (text === undefined) {
let texts = []
for (let i = 0; i < nodes.length; i++) {
texts[i] = nodes[i].textContent
}
return texts
} else {
for (let i = 0; i < nodes.length; i++) {
nodes[i].textContent = text
}
}
}
nodes.addClass = function(classes) {
for (let key in classes) {
for (let i = 0; i < nodes.length; i++) {
let methodName = classes[key] ? 'add' : 'remove'
nodes[i].classList[methodName](key)
}
}
}
return nodes
}
window.$ = window.jQuery